開始編寫Golang代碼
介紹
本文主要講述如何寫一個(gè)簡單的Go包和如何使用golang的工具熊痴,如何獲取把鉴、編譯和安裝Go的包蜂林,以及如何使用go的命令叔磷。
Go的工具需要將代碼按照一定的方式來組織阵具。所以請(qǐng)認(rèn)真閱讀本文碍遍。
代碼的組織
workspace
go工具是設(shè)計(jì)用來處理公開代碼庫的開源代碼的,雖然你不是一定要公開你的代碼阳液,但是工作的模式是一樣的怕敬。
Go代碼必須保存在一個(gè)workspace中。一個(gè)workspace必須要在根目錄下包含三個(gè)子目錄:
- src 包含了Go的源文件帘皿,這些源文件以package的方式存在
- pkg 包含了包對(duì)象
- bin 包含了可執(zhí)行文件
Go工具會(huì)編譯源文件并把編譯成的二進(jìn)制文件存放在pkg和bin目錄中东跪。
如下是一個(gè)典型的go項(xiàng)目的目錄:
bin/
hello # 命令可執(zhí)行文件<br/>
outyet # 命令可執(zhí)行文件
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # 包對(duì)象
src/
github.com/golang/example/
.git/
hello/
hello.go # 源文件
outyet/
main.go # 源文件
main_test.go # 測(cè)試 源文件
stringutil/
reverse.go # 源文件
reverse_test.go # 測(cè)試 源文件
這個(gè)workspace只包含了一個(gè)代碼庫(example),其中包含了兩個(gè)包hello和outyet以及一個(gè)庫stringutil鹰溜。
一個(gè)典型的workspace可以包含多個(gè)代碼庫虽填,以及每個(gè)庫中的多個(gè)包和庫。多數(shù)的Go開發(fā)者把這些都放在一個(gè)單獨(dú)的workspace中曹动。
包和庫是從不同的源文件包中編譯而成的斋日。我們會(huì)稍后討論這一點(diǎn)。
GOPATH環(huán)境變量
GOPATH環(huán)境變量指定的就是你的workspace的位置墓陈。這個(gè)極可能是你開發(fā)Go項(xiàng)目的時(shí)候唯一的一個(gè)需要設(shè)定的環(huán)境變量恶守。
開始前先創(chuàng)建一個(gè)workspace目錄,然后在GOPATH中指定這個(gè)workspace目錄的位置贡必。你可以把GOPATH指定在任何的你喜歡的位置兔港。
但是本文會(huì)使用$HOME/work為workspace目錄的位置。注意仔拟,這個(gè)目錄絕對(duì)不可以和你的go的安裝目錄相同衫樊。
$ mkdir $HOME/work
$ export GOPATH=$HOME/work
然后把workspace的bin目錄添加到PATH中。
$ export PATH=$PATH:$GOPATH/bin
包路徑
標(biāo)準(zhǔn)庫的包路徑都會(huì)用簡寫:“fmt”和“net/http”理逊。你自己的包就需要選擇一個(gè)路徑了橡伞。因?yàn)椋悴粫?huì)希望以后和標(biāo)準(zhǔn)庫的包或者其他
使用的包的名稱互相沖突的晋被。如果你把你的代碼保存在某代碼庫中兑徘,那么你應(yīng)該使用這個(gè)代碼庫的根作為目錄使用:
$ mkdir -p $GOPATH/src/github.com/user
你的第一個(gè)程序
要編譯、運(yùn)行你的程序羡洛,首先選擇一個(gè)包路徑(我們會(huì)使用github.com/user/hello)并在你的workspace里創(chuàng)建一個(gè)相應(yīng)的包路徑
$ mkdir $GOPATH/src/github.com/user/hello
然后挂脑,創(chuàng)建一個(gè)hello.go的文件藕漱。并在這個(gè)文件中添加如下的代碼:
package main
import “fmt”
func main() {
fmt.Println(“Hello, world!”)
}
現(xiàn)在你可以編譯并運(yùn)行你的代碼了:
go install github.com/user/hello
注意你可以在你的系統(tǒng)的任何地方來運(yùn)行這個(gè)命令。go工具可以根據(jù)GOPATH從workspace的github.com/user/hello找到源文件
上面的命令行執(zhí)行之后崭闲,會(huì)在workspace的bin目錄下生成一個(gè)可以自行文件肋联,這里是hello(或者,windows下的hello.exe)刁俭。在我們的
例子中目錄為:$GOPATH/bin/hello橄仍。
go工具只會(huì)在出錯(cuò)的時(shí)候打印輸出信息。所以牍戚,如果沒有打印任何信息的話那就是編譯成功了侮繁。
現(xiàn)在你可以運(yùn)行你的程序了:
$ $GOPATH/bin/hello
Hello, world!
或者,你已經(jīng)在PATH中添加了GOPATH/bin如孝,只需要輸入可執(zhí)行文件的名字:
$ hello
Hello, world!
如果你使用了代碼管理工具宪哩,那么這就可以初始化一個(gè)代碼庫了。添加文件第晰;并commit你的第一次更改锁孟。但是,這不是一定要的茁瘦,但是
你可以不用代碼工具編寫go代碼品抽。
$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git respository in /home/user/work/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m “initial commit”
[master (root-commit) 0b4507d] initial commit
1 file changed, 1 insertion(+)
create mode 100644 hello.go
把代碼發(fā)布到遠(yuǎn)端代碼庫,讓其他的讀者可以看到你的代碼腹躁。
你的第一個(gè)庫
接下來桑包,我們創(chuàng)建一個(gè)庫,然后在hello代碼中使用這個(gè)庫纺非。
按照常理我們需要選擇一個(gè)包路徑(我們這里使用github.com/user/stringutil)并創(chuàng)建包路徑
$ mkdir $GOPATH/src/github.com/user/stringutil
然后哑了,穿件一個(gè)文件:reverse.go, 內(nèi)容為:
package stringutil
func Reverse(s string) string {
var r = []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
現(xiàn)在,用go build命令測(cè)試一下代碼是否能夠編譯烧颖。
$ go build github.com/user/stringutil
或者你在這個(gè)包的路徑下的話只需要:
$ go build
這不會(huì)生成任何文件弱左。要生成文件的話就必須要使用go install。執(zhí)行完這個(gè)命令之后就會(huì)把這個(gè)包編譯后生成在pkg目錄中炕淮。
在stringutil編譯之后拆火,修改hello.go的代碼:
package main
import (
“fmt”
“github.com/user/stringutil”
)
func main() {
fmt.Println(stringutil.Reverse(“!oG ,olleH”))
}
任何時(shí)候go工具安裝一包或者一個(gè)二進(jìn)制文件的時(shí)候,其相關(guān)的依賴也都會(huì)一起安裝涂圆。因此们镜,當(dāng)你安裝hello的時(shí)候:
$ go install github.com/user/hello
stringutil包也會(huì)自動(dòng)安裝。運(yùn)行新版本的程序润歉,你會(huì)看到一個(gè)新的模狭,反轉(zhuǎn)的消息:
$ hello
Hello, Go!
以上的步驟都執(zhí)行完成之后,你的目錄看起來是這樣的:
bin/
hello
pkg/
linux_amd64/
github.com/user/
stringutil.a
src/
github.com/user/
hello/
hello.go
stringutil/
reverse.go
注意go install把stringutil.a放在了pkg/linux_amd64目錄中踩衩,并且會(huì)在linux_amd64下創(chuàng)建和源文件所在的目錄相同的結(jié)構(gòu)嚼鹉。
go工具之后的編譯中如果發(fā)現(xiàn)pkg中已經(jīng)存在這一文件贩汉,那么就不會(huì)再次編譯。這樣可以避免不必要的重復(fù)編譯锚赤。linux_amd64目錄是為了交叉編譯
匹舞,并且反映出所在的操作系統(tǒng)和系統(tǒng)架構(gòu)。
Go的可執(zhí)行文件是靜態(tài)鏈接的线脚。
##包名稱
Go的源文件第一行必須是:
package name
包名稱是import的時(shí)候使用的默認(rèn)名稱赐稽,同一個(gè)包中的文件必須使用同樣的包名稱。
Go的慣例是包名稱是import語句的最后一個(gè)元素浑侥。crypto/rot13引入的包就應(yīng)該命名為rot13.
main函數(shù)所在的包必須用package main作為包名稱又憨。
包的名稱沒有要求必須要唯一,但是報(bào)的整個(gè)引入路徑必須要唯一锭吨。
##測(cè)試
Go有一個(gè)輕量級(jí)的測(cè)試框架,由go test和testing包組成寒匙。
寫一個(gè)測(cè)試只需要零如,創(chuàng)建一個(gè)_test.go結(jié)尾的文件,這個(gè)文件里包含了名稱為TestXXX的方法锄弱,這些測(cè)試方法的簽名為func (t *testing.T)考蕾。
測(cè)試框架會(huì)運(yùn)行每一個(gè)這樣的方法。如果某個(gè)測(cè)試方法調(diào)用了t.Error或者t.Fail, 那么這個(gè)測(cè)試被認(rèn)為是失敗的会宪。
給stringutil添加一個(gè)測(cè)試肖卧。在$GOPATH/src/github.com/user/sringutil/目錄創(chuàng)建文件reverse_test.go赋除。這個(gè)文件包含
如下代碼:
```go
package stringutil
import “testing”
func TestReverse(t *testing.T) {
case := []struct {
in, want string
}
{
{“Hello, world”, “dlrow ,olleH”},
{“Hello, 世界”, “界世 ,olleH”},
{“”, “”},
}
for _, c := range cases {
got := Reverse(c.in)
if got != c.want{
t.Errorf(“Reverse(%q) == %q”, c.in, got, c.want)
}
}
}
然后使用命令go test來運(yùn)行測(cè)試:
$ go test github.com/user/stringutil
PASS
ok stringutil 2.223s
如果實(shí)在package內(nèi)部運(yùn)行命令的話汹忠,可以省去包的路徑。
$ go test
PASS
ok stringutil 2.223s
運(yùn)行命令go help test可以看到更多的關(guān)于測(cè)試命令的細(xì)節(jié)沪羔。
Remote packages 遠(yuǎn)端代碼庫
一個(gè)import路徑和使用Git獲取遠(yuǎn)程代碼的路徑是一樣的巍沙。go工具使用這個(gè)路徑自動(dòng)從遠(yuǎn)端代碼庫獲取代碼葵姥。比如,上面使用的代碼
托管在GitHub的路徑為:github.com/golang/example
句携。如果在代碼的import語句中包含了遠(yuǎn)端代碼榔幸,go get命令會(huì)自動(dòng)獲取、編譯
并install(安裝)這些代碼矮嫉。
$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!
如果需要的包不在workspace中削咆,go get
命令會(huì)把這些包放在GOPATH指定的第一個(gè)workspace中。如果包已經(jīng)存在蠢笋,那么go get
命令會(huì)跳過遠(yuǎn)程獲取而直接執(zhí)行go install
類似的命令拨齐。
在執(zhí)行以上的go get命令之后,workspace目錄看起來就是這樣的:
bin/
hello
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a
github.com/user/
stringutil.a
src/
github.com/golang/example/
.git/
hello/
hello.a
stringutil/
reverse.go
reverse_test.go
github.com/user/
hello/
hello.go
stringutil/
reverse.go
reverse_test.go
GitHub上托管的hello依賴于同一個(gè)包中的stringutil挺尿。hello.go中的import語句使用的是一樣的慣例奏黑。因此go get命令
可以定位并安裝(install)依賴包炊邦。
import “github.com/golang/example/stringutil
這一慣例會(huì)讓你的代碼非常容易被別人使用。
接下來熟史。馁害。。
Effective Go有更多的關(guān)于有效編寫Go代碼的內(nèi)容蹂匹。
A Tour of Go有更多的關(guān)于Go的知識(shí)碘菜。