菜单 学习猿地 - LMONKEY

 Go 官方翻译:Go 编码指南《How to Write Go Code》

Go 官方翻译:Go 编码指南《How to Write Go Code》

yutian profile image yutian ・1 min read

Golang

简介

本文档演示了一个简单 Go 包的开发,并介绍了 Go 工具,以及获取、构建和安装 Go 包和命令的标准方法。

Go 工具要求您以特定的方式组织代码。请仔细阅读这份文件。它讲解了安装 Go 时启动和运行的最简单的方法。

一个类似的讲解可以在 screencast 中找到。

代码组织

概述

  • Go 开发者通常将他们所有的 Go 代码保存在一个单一的 工作空间 中。
  • 一个工作空间包含许多版本控制的存储库 (例如,由 Git 管理)。
  • 每个存储库包含一个或多个
  • 每个包由单个目录中的一个或多个 Go 源文件组成。
  • 包目录的路径决定了它的 导入路径

这与其他编程环境不同,在其他编程环境中,每个项目都有一个独立的工作空间,工作空间与版本控制存储库紧密相连。

工作区

工作区是一个目录层次结构,在它的根目录下有两个目录:

  • src 包含Go源文件。
  • bin 包含可执行命令。

go工具构建并安装二进制文件到bin目录。

src子目录通常包含多个版本控制库 ( 如GitMercurial),用于跟踪一个或多个资源包的开发。

为了让你对工作区在实践中的样子有个概念,这里有一个例子:

bin/
    hello                          # 可执行命令
    outyet                         # 可执行命令
src/
    github.com/golang/example/
        .git/                      # Git代码库元数据
    hello/
        hello.go               # 命令源码
    outyet/
        main.go                # 命令源码
        main_test.go           # 测试源码
    stringutil/
        reverse.go             # 命令源码
        reverse_test.go        # 测试源码
    golang.org/x/image/
        .git/                      # Git代码库元数据
    bmp/
        reader.go              # 命令源码
        writer.go              # 命令源码
    ... (更多库和资源包省略) ...

上面的树显示了一个包含两个代码库的工作区 (exampleimage) 。
example代码库包含两个命令 (hellooutyet) 和一个库 (stringutil)。image代码库包含bmp包和其他几个

典型的工作区包含许多代码库,代码库又包含很多包和命令。大多数Go程序员将*all* 源代码和依赖关系保存在一个单独的工作区中。

请注意,符号链接不应该用于您的工作区。
命令和库是由不同的资源包构建的。我们稍后将讨论这种区别。

GOPATH 环境变量

环境变量 GOPATH 指定工作空间的位置。它默认在主目录中一个名为 go 的目录,所以在 Unix 中是 $HOME/go,在Plan9 中是 $home/go,在 Windows 中是 %USERPROFILE%\go (通常是 C:\Users\YourName\go)。

如果你想在一个不同的地方工作,你需要 设置 GOPATH 到那个目录的路径(另一个常见的设置是设置 GOPATH=$HOME) 。注意 GOPATH 一定不能与你的 Go 安装路径相同。

命令 go env GOPATH 打印当前有效的 GOPATH;如果环境变量未设置,则打印默认位置。

为了方便,将工作空间的 bin 子目录添加到您的 PATH 中:

$ export PATH=$PATH:$(go env GOPATH)/bin

为了简单起见,本文其余部分的脚本使用 $GOPATH 而不是 $(go env GOPATH)。如果你没有设置 GOPATH,你可以用这些命令替换 $HOME/go:

$ export GOPATH=$(go env GOPATH)

要了解更多关于 GOPATH 环境变量的信息,请参见 go help GOPATH

导入路径

导入路径是唯一标识包的字符串。一个包的导入路径对应于它在工作区或远程代码库中的位置,解释如下:

标准库中的包有给定的短路径,比如 fmt 和 net/http。对于你自己的包,你必须选择一个基本路径,来保证它不会与将来添加到标准库、或其它扩展库中的包相冲突。

如果你的代码在某个代码库中,那就应当使用该代码库的根目录作为你的基本路径。例如,若你在 GitHub 上有账户github.com/user那么它应该是你的基本路径。

注意,在你构建这些代码之前,无需公布到远程代码库上。只是如果你发布它时,这会是个好习惯。在实践中,你可以选择任何路径名,只要它对于标准库和更大的Go生态系统来说, 是唯一的就行。

我们使用 github.com/user 作为基本路径。在你的工作空间里创建一个目录,我们将源码存放到其中:

$ mkdir -p $GOPATH/src/github.com/user

你的第一个程序

要编译并运行简单的程序,首先要选择包路径(我们使用 github.com/user/hello),并在你的工作区创建相应的包目录:

$ mkdir $GOPATH/src/github.com/user/hello

接着,在该目录中创建名为 hello.go 的文件,其内容为以下Go代码:

package main

import "fmt"

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

现在你可以用 go 工具构建并安装此程序了:

$ go install github.com/user/hello

注意,你可以在系统的任何地方运行此命令。go 工具会根据 GOPATH 指定的工作空间,在 github.com/user/hello 包内查找源码。

如果在包目录中运行 go install,也可以省略包路径:

$ cd $GOPATH/src/github.com/user/hello
$ go install

此命令会构建 hello 命令,产生一个可执行的二进制文件。 接着它会将该二进制文件作为 hello(在 Windows 下则为 hello.exe)安装到工作空间的 bin 目录中。 在我们的例子中为 $GOPATH/bin/hello,也就是就是 $HOME/go/bin/hello

go 工具只有在发生错误时才会打印输出,因此若这些命令没有产生输出, 就表明执行成功了。

现在,你可以在命令行下输入它的完整路径来运行它了:

$ $GOPATH/bin/hello
Hello, world.

如果你将 $GOPATH/bin 添加到 PATH 中了,只需输入该二进制文件名即可:

$ hello
Hello, world.

若你使用源码控制系统,那现在就该初始化仓库,添加文件并提交你的第一次更改了。 再次强调,这一步是可选的:你无需使用源码控制来编写Go代码。

$ cd $GOPATH/src/github.com/user/hello
$ git init
Initialized empty Git repository in /home/user/go/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 7 insertion(+)
 create mode 100644 hello.go

将代码推送到远程仓库就留给读者来练习了。

你的第一个库

让我们编写一个库并在 hello 程序中使用它。

同样,第一步是选择一个包路径(我们将使用 github.com/user/stringutil )并创建包目录:

$ mkdir $GOPATH/src/github.com/user/stringutil

接下来,在该目录中创建一个名为 reverse.go 的文件,其内容如下。

// 包 stringutil 包含处理字符串的实用函数。
package stringutil

// Reverse 从左到右按运行方式反向返回传入的字符串。
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

这不会产生输出文件。相反,它将编译后的包保存在本地构建缓存中。

在确认构建了 stringutil 包之后,修改原始的 hello.go(在 $GOPATH/src/github.com/user/hello 里面)以使用它:

package main

import (
    "fmt"

    "github.com/user/stringutil"
)

func main() {
    fmt.Println(stringutil.Reverse("!oG ,olleH"))
}

安装 hello 程序:

$ go install github.com/user/hello

运行新版本的程序,你应该会看到一个新的反过来的消息:

$ hello
Hello, Go!

完成以上步骤后,您的工作空间应该是这样的:

bin/
    hello                 # 可执行命令
src/
    github.com/user/
        hello/
            hello.go      # 命令源码
        stringutil/
            reverse.go    # 包源码

包名

Go源文件中的第一个语句必须是

package 名称

这里的 *名称* 即为导入该包时使用的默认名称。(一个包中的所有文件都必须使用相同的 *名称*。)

Go的约定是包名为导入路径的最后一个元素:作为 “crypto/rot13” 导入的包应命名为 rot13
可执行命令必须使用 package main
链接成单个二进制文件的所有包,其包名不需是唯一的,只要导入路径(它们的完整文件名)是唯一的。

更多关于Go的命名约定见 Go 高效编程

测试

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

通过创建一个以 _test.go 结尾的文件来编写测试,该文件包含名为 TestXXX 的函数,签名为 func (t *testing.T)。测试框架会运行所有这样的函数;如果该函数调用了一个失败的函数,如 t.Errort.Fail,则认为测试失败。

通过创建包含以下 Go 代码的文件 $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 运行测试:

$ go test github.com/user/stringutil
ok      github.com/user/stringutil 0.165s

与往常一样,如果您从包目录运行 go 工具,您可以省略包路径:

$ go test
ok      github.com/user/stringutil 0.165s

运行 go help test 并查看 测试包文档 以获得更多细节。

远程包

GitMercurial这样的版本控制系统,可根据导入路径的描述来获取包源代码。go工具可通过此特性从远程代码库自动获取包。例如,本例也可存放到 GitHub上 github.com/golang/example。若你在包的导入路径中包含了代码仓库的URLgo 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                           # 可执行命令
src/
    github.com/golang/example/
    .git/                       # Git 代码库元数据
        hello/
            hello.go                # 命令源码
        stringutil/
            reverse.go              # 包源码
            reverse_test.go         # 测试源码
    github.com/user/
        hello/
            hello.go                # 命令源码
        stringutil/
            reverse.go              # 包源码
            reverse_test.go         #测试源码

hello命令及其依赖的stringutil包都在GitHub上的同一代码库中。hello.go文件使用了相同的导入路径, 因此 go get 命令也能定位并安装其依赖包。

import "github.com/golang/example/stringutil"

这样别人就可以更方便的使用你的Go包。Go维基 与 godoc.org 提供了外部Go项目的列表。

通过 go 工具使用远程代码库的更多资料,请参照 go help importpath

接下来做什么

订阅 golang-announce 邮件来获取Go的稳定版发布信息。

如何编写清晰、惯用的Go代码,请参考 高效Go编程

学习Go语言,请参考 Go语言之旅

Go语言的深入性文章、库和工具,见文档

获取帮助

如果需要实时帮助,可以请教 Freenode IRC 上 #go-nuts 中的 Gopher 们。

Go语言的官方讨论邮件列表为 Go Nuts

请使用 Go 问题跟踪器报告 Bug。

评论 (0)