如何编写Go代码

如何编写Go代码

引入

这篇文章演示了在一个模块内如何去开发一个简单的Go包,并且引入了go工具,他是用于获取,构建和安装Go模块,包和命令的标准方式。

注意:这篇文章假定你正在使用Go1.13及之后的版本,并且GO111MODULE环境变量没有被设置。

代码组织

Go程序被组织为包。一个是源文件的集合,这些源文件在同一个目录并且被一起编译。被定义在一个源文件中的函数,类型,变量和常量,对于相同包之内的其他源文件是可见的。

一个仓库包含一个或多个模块。一个模块是相关的Go包的集合,这些Go包被一起发布。一个Go仓库通常只包含一个模块,该模块位于仓库的根目录。一个名为go.mod的文件声明了模块路径:在模块内部为所有包导入路径的前缀。模块将包包含在包含其go.mod文件的目录以及该目录的子目录中,直至包含另一个go.mod文件的下一个子目录(如果有)。

注意,你不需要发布你的代码到一个远程仓库在你构建它之前。一个模块可以被定义不属于一个仓库。然而,就像你在将来的某一天要发布他一样组织好你的代码是一个好习惯。

每个模块的路径不仅充当其包的导入路径前缀,而且还指示go命令将其下载到的位置。例如,为了下载模块golang.org/x/tools,go命令将查询https://golang.org/x/tools所指示的存储库。

导入路径是一个字符串,它被用于导入一个包。一个包的导入路径是他的模块路径加上他在该模块内的子目录。例如,模块github.com/google/go-cmp包含一个包,这个包在目录cmp/中,则导入路径为github.com/google/go-cmp/cmp。在标准库中的包不需要模块路径前缀。

你的第一个程序

要编译和允许一个简单的程序,首先要选择一个模块路径(我们将使用example.com/user/hello)并创建一个go.mod文件:

$ 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 name。可执行命令必须总是使用包main。

接下来,创建一个名为hello.go的文件:

package main

import "fmt"

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

现在你可以使用go工具构建并安装这个程序:

$ go install example.com/user/hello
$

这条命令构建hello命令,产生一个可执行文件。然后并安装在$HOME/go/bin/hello目录(在Windows下为%USERPROFILE%\go\bin\hello.exe)。

安装目录被环境变量GOPATH和GOBIN控制。如果GOBIN被设置,可执行文件将被安装到这个目录。如果GOPATH被设置,可执行文件被安装到GOPATH列表的第一个目录下的子目录bin中。否则,可执行文件被安装到默认的GOPATH目录($HOME/go或%USERPROFILE%\go)下的bin子目录中。

你可以使用go env命令为将来的go命令设置环境变量的默认值:

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

使用go env -u取消之前的设置:

$ go env -u GOBIN
$

诸如go install之类的命令适用于包含当前工作目录的模块的上下文。如果工作目录不在example.com/user/hello模块内,go install将失败。

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

$ go install example.com/user/hello
$ go install .
$ go install

接下来让我们允许这个程序以确保他的工作。为了更加方便,我们将安装目录添加到PATH中,以使运行可执行文件变得更容易:

# 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代码

$ 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代码的存储库提供了该元数据,因此使您的模块可供其他人使用的最简单方法通常是使其模块路径与存储库的URL相匹配。

从你的模块导入包

让我们编写一个morestrings包并在hello程序中使用它。首先,为这个包创建一个名为$HOME/hello/morestrings的目录,然后添加一个名为reverse.go的文件:

// 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测试这个包:

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

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

在确定morestrings包被构建之后,让我们在hello程序中使用它。修改$HOME/hello/hello.go:

package main

import (
	"fmt"

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

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

安装hello程序:

$ go install example.com/user/hello

运行程序:

$ hello
Hello, Go!

从远程模块导入包

导入路径可以描述如何使用版本控制系统(例如Git或Mercurial)获取包源代码。go工具使用此属性从远程存储库自动获取软件包。例如,要在程序中使用github.com/google/go-cmp/cmp:

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文件中:

$ 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命令将这些文件和目录标记为只读。 要删除所有下载的模块,可以传递 -modcache标志给go clean:

$ 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包中。

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运行这个测试:

$ go test
PASS
ok  	example.com/user/morestrings 0.165s
$

参考资料

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

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页