原文:https://golang.org/doc/code.html

本文演示了一个包含模块(module)的简单Go包的开发,并介绍了Go的一些工具,这是获取(fetch)、构建(build)和安装(install) Go模块,包和命令的标准方式。

第一个程序

选择一个模块路径(我们将会使用example.com/user/hello),然后创建一个声明模块的go.mod文件。

1
2
3
4
5
6
7
8
$ mkdir hello # Alternatively, clone it if it already exists in version control.
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello

go 1.14

Go 源文件的第一句语句必须是包名。可执行命令则必须使用package main

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("Hello, world.")
}

安装:

1
$ go install example.com/user/hello

这个命令会生成hello命令,生成一个可执行二进制文件,然后将该二进制文件安装为$HOME/go/bin/hello(如果是Windows,则为:%USERPROFILE%\go\bin\hello.exe)。

你可以使用go env命令去设置默认的环境变量:

1
$ go env -w GOBIN=/somewhere/else/bin

要取消先前由go env -w设置的变量,可以使用go env -u

1
$ go env -u GOBIN

为了方便起见,go 命令接受相对路径,如果没有其他路径,则默认使用当前工作目录的包,因此,在我们的工作目录中,以下命令都是等效的:

1
2
3
$ go install example.com/user/hello
$ go install .
$ go install

接下来,让我们运行该程序以确保其能正常工作。为了更加方便,我们将安装目录添加到PATH中,使运行二进制文件变得容易:

1
2
3
4
5
# Windows users should consult https://github.com/golang/go/wiki/SettingGOPATH
# for setting %PATH%.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.

如果你在使用源代码版本控制系统,那么现在将是初始化仓库,添加文件并提交第一个更改的好时机。 同样,此步骤是可选的:你无需使用源代码版本控制系统来编写Go代码。

1
2
3
4
5
6
7
$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
1 file changed, 7 insertion(+)
create mode 100644 go.mod hello.go

Go命令通过请求响应的HTTPS URL并读取HTML响应中嵌入的元数据来查找包含给定模块路径的仓库(请参阅go help importpath)。许多托管服务已经为包含Go代码的仓库提供了元数据,因此使你的模块可供他人使用的最简单方法通常是使其模块路径与仓库的URL相匹配。

从你的模块中导入包

让我们编写一个morestrings包,并在hello程序中使用。首先,为名为*$ HOME/hello/morestrings*的包创建一个目录,然后在该目录中创建一个名为reverse.go的文件,其中包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings

// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(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)
}

因为ReverseRunes以大写字母开头,所以它是可导出的,即可以在其他已导入morestrings包的代码中使用。

让我们尝试一下go build编译该包:

1
2
$ cd $HOME/hello/morestrings
$ go build

这不会产生输出文件,而是将已编译的包保存在本地构建缓存中。

确认已构建morestrings包后,让我们在hello程序中使用它。为此,需要修改下原始的$HOME/hello/hello.go以使用morestrings包:

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"fmt"

"example.com/user/hello/morestrings"
)

func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

安装这个包:

1
go install example.com/user/hello

验证:

1
2
$ hello
Hello, Go!

从远程模块中导入包

导入路径可以描述如何去Git或Mercuial等版本控制系统获取包的源码。Go tool使用此属性自动的从远程仓库获取包。例如,要在程序中使用github.comn/google/go-cmp/cmp

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"

"example.com/user/hello/morestrings"
"github.com/google/go-cmp/cmp"
)

func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

当运行诸如go install, go build,或者go run等命令时,go命令会自动下载远程模块,并将其版本记录在go.mod文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ go install example.com/user/hello
go: finding module for package github.com/google/go-cmp/cmp
go: downloading github.com/google/go-cmp v0.4.0
go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.4.0
$ hello
Hello, Go!
string(
- "Hello World",
+ "Hello Go",
)
$ cat go.mod
module example.com/user/hello

go 1.14

require github.com/google/go-cmp v0.4.0

模块的依赖会自动下载到GOPATH环境变量设置的目录pkg/mod子目录中。给定版本的模块的下载内容在需要该版本的所有其他模块之间共享,因此go命令将这些文件和目录标记为只读。要删除所有下载的模块,可以使用go clean -modcache.

测试

Go有一个由go test命令和testing包组成的轻量级测试框架。

你可以通过创建一个名称以_test.go结尾的文件来编写测试,文件里包含名为TestXXX且具有签名func(t testing.T)的方法。测试框架会运行每一个这样的方法,如果该方法的调用结果为t.Error或t.Fail之类的失败调用,则认为该测试已失败。

通过创建包含以下Go代码的文件$HOME/hello/morestrings/reverse_test.go*可在morestrings包中添加一个测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := ReverseRunes(c.in)
if got != c.want {
t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
}
}
}

然后用go test运行这个测试:

1
2
3
4
$ go test
PASS
ok example.com/user/morestrings 0.165s
$

运行go help test 或查阅testing 包文档可以获得更多的细节信息。

What’s next

Subscribe to the golang-announce mailing list to be notified when a new stable version of Go is released.

See Effective Go for tips on writing clear, idiomatic Go code.

Take A Tour of Go to learn the language proper.

Visit the documentation page for a set of in-depth articles about the Go language and its libraries and tools.

Getting help

For real-time help, ask the helpful gophers in the community-run gophers Slack server (grab an invite here).

The official mailing list for discussion of the Go language is Go Nuts.

Report bugs using the Go issue tracker.