How to Write Go Code

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

Introduction(简介)

本文包含了一个简单的Go程序包和开发过程以及go tool的使用简介。

go tool要求你的代码有固定的组织形式。请认证阅读此文,它将带你如何如何最简单的开始你的go编程之旅。

当然,我们还准备了一个直观的视频

Code Organization(代码组织)

Workspaces

go tool 主要用于各种公共库中的开源代码。虽然你可能没有打算发布你的代码,但是环境配置应该是一样的。
go 代码需要组织在一个workspace中,一个workpace值得是一个层级目录结构,其根目录中包含3个基本的子目录

  • src 包含Go源代码文件(以代码包来组织,一个代码包一个子目录)
  • pkg 包含包的目标文件,类似其他语言中的编译目标文件
  • bin 包含可执行文件

go tool 将会编译源代码包并将二进制文件安装至pkg目录和bin目录
src 子目录包含并跟踪来自于各种版本控制仓库的源代码

下面是一个workspace的实际例子

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

该workspace包含一个代码库(example),两个最终命令行程序(hello和outyet)以及一个库文件(stringutil)。你可以直接参看github上的相关源代码
一个典型的workspace将会包含多个代码包和多个可执行程序,大部分Go程序猿将他们的所有go代码和依赖保存在一个单独的workspace中。

可执行程序和库文件将会从不同的源代码包组织编译,我们将在稍后讨论。

The GOPATH environment variable

GOPATH 环境变量用来指明workspace的位置,这几乎是你在开发Go程序过程中唯一需要设置的环境变量。

接下来,我们先创建一个workspace目录,并为之设置环境变量GOPATH。你的workspace可以位于任何位置,此处我们使用$HOME/work。注意该目录不能是Go的安装目录(另一个比较常见的设置是GOPATH=$HOME

$ mkdir $HOME/work
$ export GOPATH=$HOME/work

为了方便起见,可以将workspace的bin目录添加到你的PATH目录中

$ export PATH=$PATH:$GOPATH/bin

学习更多的关于GOPATH的用法,参阅go help gopath

Package paths

来自于标准库的代码包都有类似“fmt”和“net/http”这样简短的路径名。所以在你自己的代码包中,你需要尽量选择一个不会和标准库以及某些第三方库命名冲突的基础路径名。

如果你将你的源代码保存在一个代码库中,你应该使用代码库的根目录来作为你的基础路径名。比如你在GitHub上有这样的一个路径github.com/user,你应该使用它作为你代码包的基础路径。
注意一点,在你的代码可以编译通过之前,你并不需要将你在远端仓库中的代码进行发布。如果你想要某一天去发布它,这样组织代码将会是一个很好的习惯。当然实际上你可以使用任意的路径名,只要不和标准库和一些常见第三方库的命名冲突就可以。

接下来我们假设使用github.com/user作为我们的基础路径名,创建其在工作空间中对应的目录结构:

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

Your first program

接下来我们将要编译并运行一个简单的程序,首先选择一个程序包路径(这里我们使用 github.com/user/hello)并在之前设置的workspace中创建对应目录

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

下一步,在该目录下创建一个hello.go的文件,内容如下:

package main

import "fmt"

func main() {
    fmt.Printf("Hello, world.\n")
}

下面我们可以使用go tool来编译安装该程序

$ go install github.com/user/hello

注意此命令可以在你的文件系统中的任意位置运行,go tool将会通过GOPATH来在对应的workspace下查找github.com/user/hello目录。
如果你在程序包所在的目录,你可以忽略路径名来运行go install,如下所示:

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

go install命令编译了hello代码,产生了一个可执行文件,并将该可执行文件安装至workspace目录下的bin目录中,命名为hello(在windows下是hello.exe)。在我们的示例中,将是$GOPATH/bin/hello,即$HOME/work/bin/hello

go tool只有在发生错误时才会产生输出,所以如果这些命令执行过程中没有输出,以为着他们执行成功了。

接下来你可以运行刚才编译安装好的代码:

$ $GOPATH/bin/hello
Hello, world.

如果你已经把$GOPATH/bin目录添加到你的PATH目录中,你可以直接按程序名调用它:

$ hello
Hello, world.

如果你使用了一个类似git的代码控制系统,现在正是将其提交的好时机。当然,这些仅是可选的。

$ 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

将代码推倒远端仓库就作为读者自己的联系吧,此处不再赘述。

Your first library

让我们写一个代码库并在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命令才可以。在此例中他们都会在workspace的pkg目录下产生对应的目标文件。

确认了stringutil包已经正确编译之后,修改 hello.go (在$GOPATH/src/github.com/user/hello目录下)来使用我们刚刚写好的方法。

$GOPATH/src/github.com/user/hello) to use it:

package main

import (
    "fmt"

    "github.com/user/stringutil"
)

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

无论go tool是用来编译安装一个可执行文件还是代码包,他都会编译安装其依赖的的内容,所以当你安装hello程序时

$ go install github.com/user/hello

stringutil程序包会自动的编译安装完成
运行这个程序:

$ hello
Hello, Go!

经过以上几步,你的workspace应该已经变成了这样

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目录下,这样将来的引用可以直接在该目录下找到该代码包从而防止重新编译它。linux_amd64是为了协助于交叉编译过程中,指明目标操作系统以及体系结构。
Go的可执行文件采用静态链接的形式,所以实际运行时不需要额外的目标文件。

Package names

Go源文件的第一句声明必须是

package name

其中的name是代码包名(在同一代码包中的文件都应该使用相同的name
Go的传统是代码包名应该使用导入路径的最后一个元素:比如导入路径名为crypto/rot13的包名应该为rot13
可执行文件的包名始终需要是package main
对最终编译到同一个文件的所有相关依赖的包名并没有唯一性的要求,只有完整的导入路径(包括包名)有唯一性要求
更多的Go命名传统,请参阅Effective Go

Testing

Go自带一个轻量级的测试框架,由go test命令和testing包组成。
你可以创建一个后缀为 _test.go的测试文件,其中包含命名为TestXXX的类型为func (t *testing.T)的方法。该测试框架将会运行其中所有的此类方法。如果方法在运行中调用了类似t.Errort.Fail的错误提示方法,意味着这个测试方法测试失败。
下面给stringutil代码包增加一个测试函数$GOPATH/src/github.com/user/stringutil/reverse_test.go:

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 test
ok      github.com/user/stringutil 0.165s

运行go help test 以及参阅testing package documentation 来获得关于测试框架的更多帮助

Remote packages

导入路径会显示从怎样的版本控制系统(Git或者Mercurial等)能获取对应的代码包。go tool利用这一点来自动从远端仓库那里获取对应的代码包。比如我们的示例代码都保存在GitHub上github.com/golang/example。如果你在你的源代码中使用这样的导入路径,go get将能够自动的获取,编译,安装它:

$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!

如果当前的workspace中不包含导入的程序包,go get命令将会把他放置在GOPATH的第一个路径中(如果已经获取,go get跳过获取阶段,就像运行go install一样)
运行完以上的go get命令,我们的workspace应该已经看起来像这样:

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"

这是让你的代码包可以让他人复用的最简单的方法。在Go Wikigodoc.org 上列出了一些第三放Go代码。
关于更多的使用go tool的远端仓库的方法,请参阅go help importpath

What’s next

Subscribe to the golang-announce mailing list to be notified when a new stable version of Go is released.
See Effective Go for tips on writing clear, idiomatic Go code.
Take A Tour of Go to learn the language proper.
Visit the documentation page for a set of in-depth articles about the Go language and its libraries and tools.

Getting help

For real-time help, ask the helpful gophers in #go-nuts on the Freenode IRC server.
The official mailing list for discussion of the Go language is Go Nuts.
Report bugs using the Go issue tracker.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值