How to Write GO Code 中文版
-
说明
- 官方原文
- 本文按个人理解进行意译,水平有限,难免有错或偏差,欢迎批评指正
-
目录
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语言要求使用指定的方法组织代码。
- 需要认真阅读这份文档。
Code Organization
overview
通常来说Go开发者将他所有的源代码放到一个单独的工作目录中
一个工作目录中包含多版本控制仓库
每个仓库包含一个或者多个包
每个包在一个单路的目录中包含一个或者多个GO源代码文件
到包的路径决定了它import的路径
需要注意,这一点,与其他语言编程环境中每一个项目都放在一个分开的单独工作目录并且每个工作目录都有一个仓库版本控制,是不同的
workspace
一个工作目录是一个层次结构目录,在其根目录包含以下三个目录
- src 包含Go语言的源代码文件
- pkg 包含包对象
- bin 包含可执行文件
go工具构建源代码并将生成的二进制文件放到 pkg 和 bin 目录中。
src 子目录通常包含多版本控制仓库来追踪一个或者多个源包的开发过程。
下面是个实际中的关于工作空间的简要例子:
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) ...
上面的结构树展示了一个工作空间包含两个仓库(example和image)。example库包含两个可执行目录(hello和outyet)和一个库(stringutil)。image仓库包含bmp包和几个其他包
一个典型的工作空间包含了内含许多包和命令的仓库。绝大多数的go开发者将他们的源代码和依赖都放到一个工作空间中。
命令和库 是从不同的源码包构建而来。
the GOPATH environment variable
GOPATH 环境变量指明了工作空间的位置。它默认是在你的主目录中的一个名为go的目录, 所以在Linux下就是$HOME/go
, 在plan9下就是$home/go
, 在Windows下就是%USERPROFILE%/go
(通过是C:\Users\YourName\go
).
如果想在一个不同的目录下工作,需要 设置 GOPATH 到想指定的目录.(另一个通常的设置是设置 GOPATH=$HOME
).<strong>需要注意的是 GOPATH 和 go的安装目录不能相同</strong>.
为了方便,添加工作空间的 bin 子目录到path:
$ export PATH=$PATH:$(go env GOPATH)/bin
为了简短一点,上面脚本在本文末使用 $GOPATH
替换 $(go env GOPATH)
.
要学习更多关于GOPATH环境变量相关的信息,查看 go help gopath
要使用通用工作空间位置,查看 GOPATH 环境变量
import paths
一个导入路径 import path
是唯一性地标志一个包的字符串。一个包的import path对应它在工作空间中的位置或者一个远程仓库.
标准库中的包通常都给定一个比如 "fmt"或者"net/http"这样简短的import path. 对于你自己的包,需要选一个不会跟未来被添加到标准库或者其他拓展库的命名冲突的基础路径。
我们选择 github.com/user 作为我们的基础路径,然后在工作空间中创建爱你一个目录来保存源代码:
$ mkdir -p $GOPATH/src/github.com/user
your first program
为了编译和运行一个简单的程序,首先选一个包路径(我们将使用 github.com/user/hello)然后在工作目录中创建一个对应的包目录
$mkdir $GOPATH/src/github.com/user/hello
然后,在那个目录里面创建一个文件,命名为 hello.go,并把下面的代码放上去:
package main
import "fmt"
func main(){
fmt.Printf("Hello, world.\n")
}
现在,可以使用go tool 来 build 并且安装上面的程序了:
$ go install github.com/user/hello
你可以在你系统的任务地方运行上面的命令. go tool 会根据 GOPATH 指明的工作空间到 github.com/user/hello 包中找到源代码.
如果从包目录中运行 go install,你可以忽略掉包的路径:
$cd $GOPATH/src/github.com/user/hello
$go install
上面的命令将会构建 hello 命令,产生一个可执行二进制文件。然后将二进制文件以hello名字安装到工作空间的 bin 目录下(在 Windows下是 hello.exe)。在我们的例子中就是 $GOPATH/bin/hello
, 也就是 $HOME/go/bin/hello
.
go 工具只有在产生错误时才打印输出,所以如果上面的命令执行之后没有产生任何打印输出,那就是执行成功了。
现在可以通过全路径运行命令:
$ $GOPATH/bin/hello
Hello, world
或者也可以添加 $GOPATH/bin
到你的 PATH,然后运行二进制文件就可以执行命令了:
$ hello
Hello, world
your first library
选择一个包路径(在这我们将使用 github.com/user/stringutil)然后创建一个包目录来写一个库并使用:
$ mkdir $GOPATH/src/github.com/user/stringutil
接着在上面目录中创建一个名为 reverse.go 的文件并放上一下内容:
// 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 来编译上面创建的包:
$ go build github.com/user/stringutil
或者, 如果你是在上面的包的目录下的话,直接运行下面的命令:
$ go build
上面的命令不会产生一个文件。如果想要产生文件,必须使用 go install,这样的话会在工作空间的 pkg 的目录中产生一个包对象。
确认 stringutil 包被构建之后,更改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 安装了一个包或者二进制文件, 包或者二进制文件里面的所有依赖也会被安装,所以当安装 hello 程序, stringutil 包也同时会自动被安装
$go install github.com/user/hello
执行新版本的程序,应该可以看到一个新的信息
$ hello
Hello, Go!
上面所有的步骤执行完之后,工作空间看起来应该是下面这样的:
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 可以找到包对象并避免对包进行不必要的重编译。linux_amd64可以帮助交叉编译,并反映操作系统和系统的结构。
Go 命令可执行文件是静态链接的:运行 Go 程序不需要包对象的存在
package names
Go 源文件的第一条语句必须是:
package name
name 是包的导入默认名字(其他文件导入这个包时使用的名称)(一个包中的所有文件都必须是相同的name)
Go 的惯例是包名是导入路径的最后一个元素。如果被导入的包是cryto/rot13
那么包的名字应该是 rot13
。
可执行命令必须使用总是使用 package main。
被链接到一个单个二进制文件的所有包,这些包的名称不要求名字唯一性,但是包的导入路径要求内唯一性(文件完整名称)
查看 Effective Go 来了解更多信息
Testing
Go 有一个由 go test 命令和 testing 包组成的轻量级的测试框架.
创建一个以 _test.go为文件名后缀的文件,文件中包含名为 TestXXX 的签名 func (t *testing.T)的函数,通过这样的方式来编写测试。测试框架运行每一个这样的函数,如果函数调用类似 t.Error 或者 t.Fail 的报错函数,那么说明这里面对应的测试用例已经失败.
创建 $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)
}
}
}
运行下面的命令来跑测试:
$ go test github.com/user/stringutil
ok github.com/user/stringutil 0.165s
另外,如果你是在包的目录下的话,不用跑上面的命令,直接跑下面的命令即可
$ go test
ok github.com/user/stringutil 0.165s
运行 go helo test 以及查看 testing package documentation来了解更多信息
remote packages
一个导入路径可以描述如何通过使用比如git或者marcurial这样的版本控制系统来获取包的源代码。go tool 使用这个特性来自动从远处仓库获取包。举个栗子,在这个文档中描述的包同时是一个托管在 github.com/golang/example 的Git仓库。如果在包的导入路径中包含这个仓库的URL,那么, go get 会自动地获取,构建,然后安装它:
$ go get github/com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples?
如果指定的包没有出现在工作空间中,go get 将会把该指定的包放置通过GOPATH指定的第一个工作空间中。(如果该包早已经存在将要被放置的工作空间中,go get 将会跳过从远程仓库获取的过程从而继续执行go install)
执行完上面 go get 命令之后,工作空间的目录树看起来应该是下面这样的:
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 文件中的导入使用相同的导入路径约定,因此 go get 也可以找到并安装依赖的包。
import "github.com/golang/example/stringutil"
这个约定是让你的go 包可以被其他go开发者使用的最简单的方法。Go wiki和godoc.org提供里go的拓展项目.
想要了解更多的关于使用 go tool 来获取远程仓库的信息,查看go help importpath
what`s next
看一下 Effective Go
看一下 A Tour of Go
getting help
如果需要实时帮助,访问Freenode
官方关于go的探讨的邮件列表在Go Nuts
有bug想上报?到Go issue tracker