如何写 Go 程序
本文是 「Golang.org」的原文档翻译,出于本人有限的英语水平,如有不当,还请指正。
说明
本文档演示了一个最简单的 Go 包的开发过程以及一些 Go 命令行工具的使用,以及Go是如何架构、安装与搜索包的。
代码组织
GOPATH 以及工作区
Go 语言设计的目标之一是让软件开发更简单,得到的结果是 Go命令不使用任何的 Makefiles或者其它的配置文件,取而代之的是直接使用程序的源代码来管理依赖关系及架构条件,这意味着,你程序的源代码与架构脚本总是同步的,因为它们就是同样的东西(注:如果使用 Makefiles之类的东西,那么,你源代码修改了,还需要再去修改Makefiles,而是在Go中,因为没有这些配置文件,所以,你修改源代码,同时直接就修改了这些配置)。
你唯一需要做的是设置一个 GOPATH 环境变量,GOPATH 的作用是告诉 Go 从哪里去搜索Go的执行脚本以及将你的Go安装到哪里.$GOPATH 是一个包括很多路径的列表,它的格式与系统的PATH变量格式一样,一个Unix系统中常见的GOPATH像这样的:
GOPATH=/home/user/ext:/home/user/mygo
(在Windows系统中,将“:”修改为“;”)
任何一个被定义至 $GOPATH$ 环境变量中的路径都指定着一个工作区的的位置(比如上例中的 /home/user/ext或者/home/user/mygo),一个工作区包含着Go源代码以及它们所依赖的包对象以及一些可执行的工具,它被规定为需要有下面这三个子目录:
- src : Go 源代码文件
- pkg : 以编译的 Go 包
- bin : 可执行命令
src 目录中的子目录都是独立的包,这些包中的所有源代码文件(.go,.co,.h以及.s等)都是子目录包的元素。
它构建一个import "widget"的程序时,Go构建工具会搜索 Go 根目录下的 src/pkg/widget 目录——如果没有找到,那么它会接着尝试去寻找同一工作区中的 src/widget 目录。
多个工作区可以给我们带来更我铁灵活性与方便,但是在本文档中,我们只涉及单个工作区,下面让我们先创建一个示例工作区,首先创建一个目录,我们用它来存放项目的源代码:
cox@CoxStation:~$ mkdir $HOME/example/src -p
下一步,设置 GOPATH 环境变量,你需要同时将 $GOPATH/bin 添加到 PATH 环境变量中,这样一来,你就不需要每次执行这些命令的时候输入完整的绝对路径了。
cox@CoxStation:~$ export GOPATH=$HOME/example
cox@CoxStation:~$ export PATH=$PATH:$HOME/example/bin
导入路径
Go 标准库或者包都可以使用最短的路径名导入,比如 fmt 或者 net/http 等,这是为了程序开发者的方便而设计的,但是对于你自己的项目,你需要认真的设计一个基本的导入路径,这需要考虑它它的可读性同时还需要考虑到它不应该和以有的或者将来会出现的其它的库相冲突。
最好的一个办法是总是以你所开发的项目的版本管理工具名称开始,比如,如果你的项目源代码库位置为 example.com 或者 code.google.com/p/example ,那么你应该让你的包路径从该 URL 地址开始,比如:example.com/foo/bar 或者 code.google.com/p/example/foo/bar ,如果你遵循这样的约定,那么 Go 会自动为你检测URL地址所指定的包,同时下载以及以你的导入路径安装至你的工作区中。
如果你没有像上面这样的发布路径,那么你至少应该使用一个唯一的前缀,比如 widget 以及 widget/foo/bar ,一个好的规则是使用你的项目名称或者公司名称作为前缀,这一般是不会被别人使用的,比如:example.com。
我们下面使用 example.com 作为基本的导入路径:
cox@CoxStation:~$ mkdir -p $GOPATH/src/example.com
包名称
任何一个 Go 源代码文件的第一个声明都应该是包名:
package NAME
NAME 就是你的包名称,它也是你导入该包时默认使用的名称(所有处于同一个包中的文件都必须共享同一个包名)。
Go 约定包名称是导入路径中的最后一个元素,一个以 crypto/rot12 导入的包的包名应该是 rot13,Go 不要求所有被导入到某一个程序中的包名都具有唯一性,但是要求其完整的导入路径必须是唯一的。
在 example 中创建一个新包,名为 newmath:
cox@CoxStation:~$ cd $GOPATH/src/example.com
cox@CoxStation:~/example/src/example.com$ mkdir newmath
之后创建一个名为 $GOPATH/src/example.com/newmath/sqrt.go 的文件,它的内容为:
// Package newmath is a trivial example package.
package newmath
// Sqrt returns an approximation to the square root of x.
func Sqrt(x float64) float64 {
// This is a terrible implementation.
// Real code should import "math" and use math.Sqrt.
z := 0.0
for i := 0; i < 1000; i++ {
z -= (z*z - x) / (2 * x)
}
return z
}
该包将可以使用下面这个路径导入:
import "example.com/newmath"
构建以及安装
Go 命令包含了很多子命令,最核心的子命令就是install,运行go install importpath 将构建并安装它所依赖的所有包。
安装一个包 表示将构建之后的包文件或者可执行的文件写入到当前工作区的pkg或者bin目录中。
构建一个包
要构建并安装 newmath包,使用下面命令:
cox@CoxStation:~/example$ go install example.com/newmath
如果构建成功,那么该命令不会有任何输出。Go约定,如果没有指定导入路径,那么go使用当前目录,下面的这两行命令与上面那个是一样的效果:
$ cd $GOPATH/src/example.com/newmath
$ go install
构建之后的结果是go为你创建了一个目录树(假定我们现在使用的是Linux 64位系统——我就是这样的系统),它看起来像是这样的:
$GOPATH/
pkg/
linux_amd64/
example.com/
newmath.a
src/
example.com/
newmath/
sqrt.go
构建一个命令工具
Go 命令将包名为 main 的包视为可执行的,从而会将它安装到 $GOPATH/bin 目录中,创建一个名为 hello 的可执行命令,我们首先需要创建它的包,之后再创建包文件:
cox@CoxStation:~/example$ mkdir $GOPATH/src/example.com/hello
cox@CoxStation:~/example$ vi $GOPATH/src/example.com/hello/hello.go
文件内容为:
// Hello is a trivial example of a main package.
package main
import (
"example.com/newmath"
"fmt"
)
func main() {
fmt.Printf("Hello, Golang. Sqrt(2) = %vn", newmath.Sqrt(2))
}
保存上面文件之后,我们运行下面这个命令安装它:
cox@CoxStation:~/example$ go install example.com/hello
之后你就可以像运行其它命令一样的运行我们的 hello 了:
cox@CoxStation:~/example$ $GOPATH/bin/hello
Hello, Golang. Sqrt(2) = 1.414213562373095
如果你将 $GOPATH/bin 目录添加到了全局环境变量 PATH 中的话,那么你还可以像下面这样的执行:
cox@CoxStation:~/example$ export PATH=$PATH:$HOME/example/bin/
cox@CoxStation:~/example$ hello
Hello, Golang. Sqrt(2) = 1.414213562373095
现在我们的示例程序的目录数看起来应该像下面这个样子的了:
$GOPATH/
bin/
hello
pkg/
linux_amd64/
example.com/
newmath.a
src/
example.com/
hello/
hello.go
newmath/
sqrt.go
测试
Go 内置了一个轻量级的测试框架,它由 go test 命令和 testing 包组成。
创建一个以 _test.go 结尾的文件,让它包含只有用来标记的参数 t *testing.T的名为TestFUNC* 的函数,那么测试框架就会执行每一个像上面这样的函数,其实函数名称中的 FUNC 修改成为你需要测试的函数名,如果该函数调用了失败函数,比如t.Error或者t.Fail,那么表示整个测试失败。
我们现在创建一个名为 $GOPATH/src/example.com/newmath/sqrt_test.go 的测试文件用来测试 Sqrt函数:
cox@CoxStation:~/example$ vi $GOPATH/src/example.com/newmath/sqrt_test.go
它的内容为:
package newmath
import "testing"
func TestSqrt(t *testing.T) {
const in, out = 4, 2
if x := Sqrt(in); x != out {
t.Errorf("Sqrt(%v) = %v, want %v", in, x, out)
}
}
现在运行测试:
cox@CoxStation:~/example$ go test example.com/newmath
ok example.com/newmath 0.005s
远程包
一个导入路径可以告诉 go 如何从一个版本管理系统(比如Git或者Mercurial)中获取包代码,go命令使用这些属性自动从远程搜索包,比如我们的这个示例程序,它的远程地址是:code.google.com/p/go.example,如果你将该URL址址告诉go,那么go会自动从远程下载该包的源代码:
$ go get code.google.com/p/go.example/hello
$ $GOPATH/bin/hello
Hello, world. Sqrt(2) = 1.414213562373095
如果指定的远程包在本地没有复本,那么 go 会自动的为你下载它,如果已经存在,则直接跳过,运行了上面的命令之后,你的目录看起来应该像下面这样的了。
$GOPATH/
bin/
hello
pkg/
linux_amd64/
code.google.com/p/go.example/
newmath.a
example/
newmath.a
src/
code.google.com/p/go.example/
hello/
hello.go
newmath/
sqrt.go
sqrt_test.go
example/
hello/
hello.go
newmath/
sqrt.go
sqrt_test.go
如果你不直接告诉 go 去下载安装某个包,而是在某一个你所写的包中导入它,比如:
import "code.google.com/p/go.example/newmath"
那么,go 一样的也会自动的为你做好这一切。
说明
- 本翻译文档中所涉及的代码和原始文档有些话差入。
- 你可以从这里下载到本文档中所创建的示例代码:http://dl.antusoft.com/examples/golang/go.example.tar.gz
- 官方示例项目地址为:http://code.google.com/p/go.example/
评论已关闭