如何编写Go代码

87 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

1. 代码组织

Go程序使用包(package)进行组织。

一个包就是放在同一目录下、一起编译的一组源文件的集合。

一个源文件中定义的函数、类型、变量和常量对于同一个包内的其他文件都可见。

一个仓库(repository)包含一个或多个模块(module)。

一个模块是一组相关的、一起发布的包的集合。

一个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

标准库中的包没有模块路径的前缀。

Go源文件中的第一句声明必须是package name,可执行命令必须使用package main

使用go install命令完成程序的编译与安装,安装目录取决于GOPATHGOPATH两个环境变量。

  • 如果GOBIN设置了,可执行文件就放在这个目录;
  • 如果GOPATH设置了,可执行文件就放在该目录下的bin子目录中。
  • 可以通过命令go env -w GOBIN=/somewhere/else/bin进行设置。

2. 导入自己模块中的包

  1. 创建一个模块example/hello

mkdir hello && cd hello go mod init example/hello

package main

import (
	"fmt"

	"example/hello/morestrings"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}
  1. 准备一个新的包

mkdir morestring && cd morestrings

// 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)
}

3. 导入远程模块中的包

修改程序调用远程仓库的包

package main

import (
	"fmt"

	"example/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.mod文件中记录对应的版本信息; 因为导入路径中描述了如何获取包的源码,使用go mod tidy命令会自动下载缺失的模块并移除那些不再使用的模块的requirements。 模块会被下载到GOPATHpkg/mod目录,并且被所有需要这一版本的其它模块所共享。Go也因此将这些文件和目录设置为只读,移除这些下载的模块需要使用go clean -modcache命令。

4. 测试

Go提供了一个轻量的测试框架,由go test命令和testing包组成。 测试文件的要求:

  • 命名xxx_test.go
  • 包含名为TestXxx,签名为func (t *testing.T)的函数
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)
		}
	}
}