How to Write GO Code 中文版
-
說明
- 官方原文
- 本文按個人理解進(jìn)行意譯,水平有限,難免有錯或偏差,歡迎批評指正
-
目錄
Introduction
-
Code Organization
- overview
- workspace
- the GOPATH environment variable
- import paths
- your first program
- your first library
- package names
Testing
remote packages
what`s next
getting help
introduction
- Go語言要求使用指定的方法組織代碼。
- 需要認(rèn)真閱讀這份文檔。
Code Organization
overview
通常來說Go開發(fā)者將他所有的源代碼放到一個單獨(dú)的工作目錄中
一個工作目錄中包含多版本控制倉庫
每個倉庫包含一個或者多個包
每個包在一個單路的目錄中包含一個或者多個GO源代碼文件
到包的路徑?jīng)Q定了它import的路徑
需要注意,這一點(diǎn),與其他語言編程環(huán)境中每一個項(xiàng)目都放在一個分開的單獨(dú)工作目錄并且每個工作目錄都有一個倉庫版本控制,是不同的
workspace
一個工作目錄是一個層次結(jié)構(gòu)目錄,在其根目錄包含以下三個目錄
- src 包含Go語言的源代碼文件
- pkg 包含包對象
- bin 包含可執(zhí)行文件
go工具構(gòu)建源代碼并將生成的二進(jìn)制文件放到 pkg 和 bin 目錄中。
src 子目錄通常包含多版本控制倉庫來追蹤一個或者多個源包的開發(fā)過程。
下面是個實(shí)際中的關(guān)于工作空間的簡要例子:
bin/
hello # command executable
outyet # command executable
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # package object
src/
github.com/golang/example/
.git/ # Git repository metadata
hello/
hello.go # command source
outyet/
main.go # command source
main_test.go # test source
stringutil/
reverse.go # package source
reverse_test.go # test source
golang.org/x/image/
.git/ # Git repository metadata
bmp/
reader.go # package source
writer.go # package source
... (many more repositories and packages omitted) ...
上面的結(jié)構(gòu)樹展示了一個工作空間包含兩個倉庫(example和image)。example庫包含兩個可執(zhí)行目錄(hello和outyet)和一個庫(stringutil)。image倉庫包含bmp包和幾個其他包
一個典型的工作空間包含了內(nèi)含許多包和命令的倉庫。絕大多數(shù)的go開發(fā)者將他們的源代碼和依賴都放到一個工作空間中。
命令和庫 是從不同的源碼包構(gòu)建而來。
the GOPATH environment variable
GOPATH 環(huán)境變量指明了工作空間的位置。它默認(rèn)是在你的主目錄中的一個名為go的目錄, 所以在Linux下就是$HOME/go
, 在plan9下就是$home/go
, 在Windows下就是%USERPROFILE%/go
(通過是C:\Users\YourName\go
).
如果想在一個不同的目錄下工作,需要 設(shè)置 GOPATH 到想指定的目錄.(另一個通常的設(shè)置是設(shè)置 GOPATH=$HOME
).<strong>需要注意的是 GOPATH 和 go的安裝目錄不能相同</strong>.
為了方便,添加工作空間的 bin 子目錄到path:
$ export PATH=$PATH:$(go env GOPATH)/bin
為了簡短一點(diǎn),上面腳本在本文末使用 $GOPATH
替換 $(go env GOPATH)
.
要學(xué)習(xí)更多關(guān)于GOPATH環(huán)境變量相關(guān)的信息,查看 go help gopath
要使用通用工作空間位置,查看 GOPATH 環(huán)境變量
import paths
一個導(dǎo)入路徑 import path
是唯一性地標(biāo)志一個包的字符串。一個包的import path對應(yīng)它在工作空間中的位置或者一個遠(yuǎn)程倉庫.
標(biāo)準(zhǔn)庫中的包通常都給定一個比如 "fmt"或者"net/http"這樣簡短的import path. 對于你自己的包,需要選一個不會跟未來被添加到標(biāo)準(zhǔn)庫或者其他拓展庫的命名沖突的基礎(chǔ)路徑。
我們選擇 github.com/user 作為我們的基礎(chǔ)路徑,然后在工作空間中創(chuàng)建愛你一個目錄來保存源代碼:
$ mkdir -p $GOPATH/src/github.com/user
your first program
為了編譯和運(yùn)行一個簡單的程序,首先選一個包路徑(我們將使用 github.com/user/hello)然后在工作目錄中創(chuàng)建一個對應(yīng)的包目錄
$mkdir $GOPATH/src/github.com/user/hello
然后,在那個目錄里面創(chuàng)建一個文件,命名為 hello.go,并把下面的代碼放上去:
package main
import "fmt"
func main(){
fmt.Printf("Hello, world.\n")
}
現(xiàn)在,可以使用go tool 來 build 并且安裝上面的程序了:
$ go install github.com/user/hello
你可以在你系統(tǒng)的任務(wù)地方運(yùn)行上面的命令. go tool 會根據(jù) GOPATH 指明的工作空間到 github.com/user/hello 包中找到源代碼.
如果從包目錄中運(yùn)行 go install,你可以忽略掉包的路徑:
$cd $GOPATH/src/github.com/user/hello
$go install
上面的命令將會構(gòu)建 hello 命令,產(chǎn)生一個可執(zhí)行二進(jìn)制文件。然后將二進(jìn)制文件以hello名字安裝到工作空間的 bin 目錄下(在 Windows下是 hello.exe)。在我們的例子中就是 $GOPATH/bin/hello
, 也就是 $HOME/go/bin/hello
.
go 工具只有在產(chǎn)生錯誤時才打印輸出,所以如果上面的命令執(zhí)行之后沒有產(chǎn)生任何打印輸出,那就是執(zhí)行成功了。
現(xiàn)在可以通過全路徑運(yùn)行命令:
$ $GOPATH/bin/hello
Hello, world
或者也可以添加 $GOPATH/bin
到你的 PATH,然后運(yùn)行二進(jìn)制文件就可以執(zhí)行命令了:
$ hello
Hello, world
your first library
選擇一個包路徑(在這我們將使用 github.com/user/stringutil)然后創(chuàng)建一個包目錄來寫一個庫并使用:
$ mkdir $GOPATH/src/github.com/user/stringutil
接著在上面目錄中創(chuàng)建一個名為 reverse.go 的文件并放上一下內(nèi)容:
// Package stringutil contains utility functions for working with strings.
package stringutil
// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
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)
}
然后 使用 go build 來編譯上面創(chuàng)建的包:
$ go build github.com/user/stringutil
或者, 如果你是在上面的包的目錄下的話,直接運(yùn)行下面的命令:
$ go build
上面的命令不會產(chǎn)生一個文件。如果想要產(chǎn)生文件,必須使用 go install,這樣的話會在工作空間的 pkg 的目錄中產(chǎn)生一個包對象。
確認(rèn) stringutil 包被構(gòu)建之后,更改hello.go(在$GOPATH/src/github.com/user/hello
目錄下)來使用它
package main
import (
"fmt"
"github.com/user/stringutil"
)
func main() {
fmt.Printf(stringutil.Reverse("!oG ,olleH"))
}
任何時候 go tool 安裝了一個包或者二進(jìn)制文件, 包或者二進(jìn)制文件里面的所有依賴也會被安裝,所以當(dāng)安裝 hello 程序, stringutil 包也同時會自動被安裝
$go install github.com/user/hello
執(zhí)行新版本的程序,應(yīng)該可以看到一個新的信息
$ hello
Hello, Go!
上面所有的步驟執(zhí)行完之后,工作空間看起來應(yīng)該是下面這樣的:
bin/
hello # command executable
pkg/
linux_amd64/ # this will reflect your OS and architecture
github.com/user/
stringutil.a # package object
src/
github.com/user/
hello/
hello.go # command source
stringutil/
reverse.go # package source
注意 go install 將 stringutil.a 對象放在 pkg/linux_amd64 里面的一個目錄下,來映射它的源目錄。這樣方便以后 go tool 可以找到包對象并避免對包進(jìn)行不必要的重編譯。linux_amd64可以幫助交叉編譯,并反映操作系統(tǒng)和系統(tǒng)的結(jié)構(gòu)。
Go 命令可執(zhí)行文件是靜態(tài)鏈接的:運(yùn)行 Go 程序不需要包對象的存在
package names
Go 源文件的第一條語句必須是:
package name
name 是包的導(dǎo)入默認(rèn)名字(其他文件導(dǎo)入這個包時使用的名稱)(一個包中的所有文件都必須是相同的name)
Go 的慣例是包名是導(dǎo)入路徑的最后一個元素。如果被導(dǎo)入的包是cryto/rot13
那么包的名字應(yīng)該是 rot13
。
可執(zhí)行命令必須使用總是使用 package main。
被鏈接到一個單個二進(jìn)制文件的所有包,這些包的名稱不要求名字唯一性,但是包的導(dǎo)入路徑要求內(nèi)唯一性(文件完整名稱)
查看 Effective Go 來了解更多信息
Testing
Go 有一個由 go test 命令和 testing 包組成的輕量級的測試框架.
創(chuàng)建一個以 _test.go為文件名后綴的文件,文件中包含名為 TestXXX 的簽名 func (t *testing.T)的函數(shù),通過這樣的方式來編寫測試。測試框架運(yùn)行每一個這樣的函數(shù),如果函數(shù)調(diào)用類似 t.Error 或者 t.Fail 的報錯函數(shù),那么說明這里面對應(yīng)的測試用例已經(jīng)失敗.
創(chuàng)建 $GOPATH/src/github.com/user/stringutil/reverse_test.go
文件,把下面的代碼放上去,這樣就為 stringutil包添加了測試.
package stringutil
import "testing"
func TestReverse(t *testing.T) {
cases := []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, want %q", c.in, got, c.want)
}
}
}
運(yùn)行下面的命令來跑測試:
$ go test github.com/user/stringutil
ok github.com/user/stringutil 0.165s
另外,如果你是在包的目錄下的話,不用跑上面的命令,直接跑下面的命令即可
$ go test
ok github.com/user/stringutil 0.165s
運(yùn)行 go helo test 以及查看 testing package documentation來了解更多信息
remote packages
一個導(dǎo)入路徑可以描述如何通過使用比如git或者marcurial這樣的版本控制系統(tǒng)來獲取包的源代碼。go tool 使用這個特性來自動從遠(yuǎn)處倉庫獲取包。舉個栗子,在這個文檔中描述的包同時是一個托管在 github.com/golang/example 的Git倉庫。如果在包的導(dǎo)入路徑中包含這個倉庫的URL,那么, go get 會自動地獲取,構(gòu)建,然后安裝它:
$ go get github/com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples?
如果指定的包沒有出現(xiàn)在工作空間中,go get 將會把該指定的包放置通過GOPATH指定的第一個工作空間中。(如果該包早已經(jīng)存在將要被放置的工作空間中,go get 將會跳過從遠(yuǎn)程倉庫獲取的過程從而繼續(xù)執(zhí)行g(shù)o install)
執(zhí)行完上面 go get 命令之后,工作空間的目錄樹看起來應(yīng)該是下面這樣的:
bin/
hello # command executable
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # package object
github.com/user/
stringutil.a # package object
src/
github.com/golang/example/
.git/ # Git repository metadata
hello/
hello.go # command source
stringutil/
reverse.go # package source
reverse_test.go # test source
github.com/user/
hello/
hello.go # command source
stringutil/
reverse.go # package source
reverse_test.go # test source
在GitHub上托管的hello命令依賴于同一個存儲庫中的stringutil包. hello.go 文件中的導(dǎo)入使用相同的導(dǎo)入路徑約定,因此 go get 也可以找到并安裝依賴的包。
import "github.com/golang/example/stringutil"
這個約定是讓你的go 包可以被其他go開發(fā)者使用的最簡單的方法。Go wiki和godoc.org提供里go的拓展項(xiàng)目.
想要了解更多的關(guān)于使用 go tool 來獲取遠(yuǎn)程倉庫的信息,查看go help importpath
what`s next
看一下 Effective Go
看一下 A Tour of Go
getting help
如果需要實(shí)時幫助,訪問Freenode
官方關(guān)于go的探討的郵件列表在Go Nuts
有bug想上報?到Go issue tracker