Go 文档 - 如何写 Go 代码

原文地址

1. 引言

本文档展示了一个简单 Go 包的开发,并介绍了用 go工具 来获取、 构建并安装 Go 包及命令的标准方式。

go 工具需要你按照指定的方式来组织代码。请仔细阅读本文档, 它说明了如何以最简单的方式来准备并运行你的 Go 安装。

视频讲解可在 此处 观看。

2. 代码的组织

2.1 概述

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

请注意,这与其他编程环境不同,在这些编程环境中,每个项目都有一个单独的工作空间,工作空间与版本控制仓库紧密相关。

2.2 工作空间 Workspace

工作空间就是一个目录,其根目录中包含三个子目录:

  • src 目录包含 Go 的源文件
  • pkg 目录包含包对象
  • bin 目录包含可执行命令

go 工具用于构建源码包,并将其生成的二进制文件安装到 pkg 和 bin 目录中。

src 子目录通常包会含多种版本控制的代码仓库(例如 Git 或 SVN), 以此来跟踪一个或多个源码包的开发。

下例展现了实践中工作空间的层次结构:

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),两个命令(hello 和 outyet) 以及一个库(stringutil)。image 仓库包含 bmp 包和几个其他的。

典型的工作空间包含许多源代码库,这些源代码库包含许多包和命令。大多数 Go 程序员将他们所有的 Go 源代码和依赖关系保存在一个工作空间中。

命令和库从不同的源码包编译而来。稍后我们会对讨论它的特性。

2.3 GOPATH 环境变量

GOPATH 环境变量指定了你的工作空间位置。默认是你的家目录下的 go 目录,在 Unix 中是 $HOME/go,在 Plan 9 中是 $home/go,在 Windows 中是%USERPROFILE%\go (通常是 C:\Users\YourName\go)。

使用默认的工作空间时,不需要设置 GOPATH。但如果你想在其他位置写代码,必须 设置 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)

通过 go help gopath 可以查看 GOPATH 环境变量的更多信息,参考这里

要使用自定义的工作空间,需要设置 GOPATH 环境变量

2.4 包路径

import path 是可以唯一标识一个包的字符串。包的 import path 和包在工作空间或远程仓库(下面会讲)中的位置相关。

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

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

请注意,在可以构建代码之前,不需要将代码发布到远程代码库删。如果代码最后需要发布,组织代码就是一个好习惯。在实践中,你可以选择任意路径名称,只要它对于标准库和更大的 Go 生态系统来说是独一无二的。

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

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

2.5 第一个程序

要编译并运行简单的程序,首先要选择包路径(这里使用 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 工具构建并安装程序:

$ 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/work/src/github.com/user/hello/.git/
$ git add hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 1 insertion(+)
  create mode 100644 hello.go

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

2.6 第一个库

让我们编写一个库,并让 hello 程序使用。

同样,第一步还是选择包路径(我们将使用 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 工具都会安装它所依赖的任何东西。因此通过下面命令来安装 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 工具,能找到包对象并避免不必要的重新编译的原因。linux_amd64 这部分能帮助跨平台编译,并反映出你的操作系统和架构。

Go 的可执行命令是静态链接的;运行 Go 程序时,包对象无需存在。

2.7 Package 名字

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

package name

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

Go 的约定是导入路径的最后一个元素为包名:作为 “crypto/rot13” 导入的包应命名为 rot13

可执行命令必须使用 package main

链接成单个二进制文件的所有包的包名无需是唯一的,只有导入路径(它们的完整文件名) 才是唯一的。

共多关于 Go 的命名约定见 高效 Go

3. 测试

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

可以通过创建一个名字以 _test.go 结尾的,包含名为 TestXXX 且声明为 func (t *testing.T) 函数的文件来编写测试。测试框架会运行每一个这样的函数;若该函数调用了像 t.Errort.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 运行测试:

$ 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 或从 testing 包文档 中查看。

4. 远程包

导入路径描述了如果从 Git 或 Mercurial 这样的版本控制系统获取包的源代码。go 工具可通过此特性自动从远程代码库获取包。例如,本文档中描述的例子也存放在GitHub 上的仓库 github.com/golang/example 中,若你在包的导入路径中包含了代码仓库的 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"

This convention is the easiest way to make your Go packages available for others to use. The Go Wiki and godoc.org provide lists of external Go projects.
遵循此约定是最简单的让你的 Go 包可以被别人使用的方式。Go Wikegodoc.org 提供了外部 Go 项目的列表。

要了解更多通过 go 工具使用远程代码库的知识,参考 go help importpath

5. 下一步

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

关于如何编写清晰、完美的 Go 代码的技巧,见 高效 Go

要学习 Go 语言,参考 Go 语言教程

关于 Go 语言的深入性文章及其库和工具,见 文档页面

6. 获取帮助

要获取实时帮助,请询问 Freenode IRC 服务器上 #go-nuts 中的 Gopher 们。

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值