如何編寫Go代碼

簡介

本文演示了一個簡單的Go語言包的開發,以及 go tool 命令的使用,包含:獲取、構建、安裝Go包和命令的標準方法。php

go tool 要求用特別的方式來組織你的Go代碼。仔細閱讀本文,它解釋了啓動和運行Go安裝的最簡單方法。html

代碼組織

概述

  • 程序員一般會將全部Go代碼保存在一個工做區中
  • 工做空間包含許多版本控制的倉庫(repo)(例如,由Git管理的)
  • 每一個倉庫包含一個或多個包
  • 每一個包由一個目錄中的一個或多個Go源文件組成
  • 包目錄的路徑肯定其導入路徑

請注意,這與其餘編程環境不一樣,在這些環境中,每一個項目都有一個單獨的工做區,工做區與版本控制倉庫緊密相關。git

工做區

工做空間是一個目錄層次結構,其根目錄有兩個目錄:程序員

  • src 用於存放Go原文件
  • bin 存放可執行的命令

go工具構建二進制文件並將其安裝到 bin 目錄。github

src 子目錄一般包含多個版本控制的倉庫(例如Git),用於跟蹤一個或多個源包的開發。golang

在實踐中,工做區應該是什麼樣子呢? 下面給出一個例子:編程

bin/
    hello                          # command executable
    outyet                         # command executable
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) ...

上面的這個樹型結構展現出一個工做區包含了2個倉庫(exampleimage)。example 倉庫包含了2個命令(hellooutyet)和一個庫(stringutil)。image 倉庫包含了 bmp 包和 一些其餘的包。vim

一般工做區會包含不少的源倉庫(包含須要多和命令)。大多數的Go開發者都會把他們的源代碼和依賴存放在一個工做區。windows

命令和庫是從不一樣類型的源包構建的。咱們稍後會討論這種區別。緩存

GOPATH 環境變量

GOPATH 環境變量用來指定工做區的位置。默認是用戶主目錄的 go 目錄,如在Linux和macOS上是 $HOME/go, 在Windows上是 C:\Users\YourName\go

go env GOPATH 命令會打印出當前有效的 GOPATH; 若是環境變量沒有被設置會打印出默認的位置。

爲方便起見,將工做空間的 bin 子目錄添加到 PATH

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

爲簡潔起見,本文檔其他部分中的腳本使用 $GOPATH 而不是 $(go env GOPATH)。即

$ export PATH=$PATH:$GOPATH/bin

而若是尚未設置 $GOPATH 就運行寫好的腳本,你須要替換爲 $HOME/go, 不然須要執行:

$ export GOPATH=$(go env GOPATH)

要學習更多關於gopath環境變量,可使用查看幫助 go help gopath

要使用自定義的工做區,能夠查看 https://golang.org/wiki/SettingGOPATH

導入路徑

導入路徑(import path)是惟一標識包的字符串。包的導入路徑對應於其在工做空間內或遠程倉庫中的位置(以下所述)。

標準庫中的包具備簡短的導入路徑,例如 fmtnet/http。對於咱們本身開發的包您必須選擇一個基本路徑,該路徑不太可能與未來添加到標準庫或其餘外部庫中發生衝突。

若是將代碼保存在某個源倉庫中,則應使用該源倉庫的根做爲基本路徑。例如,若是你在 github.com/user 上有一個GitHub賬戶,那麼這應該是你的基本路徑。

請注意,在構建代碼以前,無需將代碼發佈到遠程倉庫。組織代碼只是一個好習慣,好像有一天你會發布它同樣。實際上,你能夠選擇任意路徑名稱,只要它對標準庫和更大的Go生態系統是惟一的。

咱們將使用 github.com/user 做爲咱們的基本路徑。在工做區內建立一個目錄,用於保存源代碼:

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

你的第一個Go程序

要編譯和運行一個簡單的程序,首先要選擇一個包路徑(咱們會使用 github.com/user/hello),而後在工做區裏建立一個相應的包目錄:

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

接下來,在hello目錄裏建立一個 hello.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 命令,生成一個可執行的二進制文件。同時安裝到工做區目錄下的 bin 目錄,生成的可執行文件是 hello(若是是windows則是 hello.exe)。
在本例子中是 $GOPATH/bin/hello, 也就是 $HOME/go/bin/hello

當有錯誤發生的時候,go工具僅會打印除錯誤,因此若是沒有任何輸出的時候說明已經執行成功。

能夠經過全路徑來運行:

$ $GOPATH/bin/hello
Hello, world.

若是已經把 $GOPATH/bin 加入到了 PATH, 能夠直接輸入二進制文件名:

$ hello
Hello, world.

若是你正在使用一個版本控制系統,好比Git,如今是時候來初始化來生成一個倉庫(repository),而後添加文件,作第一次提交。

固然這一步是可選的,不必定非要使用版本控制系統來寫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
$ git push origin master

你的第一個庫

咱們再來寫一個庫,並在 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

若是已經在 github.com/user/stringutil 目錄裏,則直接執行:

$ go build

固然該命令不會生成文件,而是把編譯好的包放到了本地的構建(build)緩存裏。
確實 stringutil 包被編譯後, 修改 hello.go:

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

修改後的:

package main

import (
    "fmt"

    "github.com/user/stringutil"
)

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

再次安裝

$ go install github.com/user/hello

執行:

$ hello
Hello, Go!

經過上面的一些步驟後,如今咱們的結構是這樣子的:

bin/
    hello                 # command executable
src/
    github.com/user/
        hello/
            hello.go      # command source
        stringutil/
            reverse.go    # package source

包名

在Go原文件中第一個使用的語句必須是

package name

其中 name 就是包的默認名稱。一個包中的全部文件必須使用相同的包名。

Go的約定是包名稱是導入路徑的最後一個元素,例如導入的包 crypto/rot13, 包名就是 rot13

若是是可執行的文件,包名必須使用 main

不強制要求全部的包名都是惟一的,可是要求導入的路徑必須是惟一的(全路徑文件名)。

更多關於go的命名規範能夠查看 Effective Go

測試

Go提供了一個由 go testtesting 包組成的測試框架。

經過建立一個以 _test.go 結尾的文件,裏面寫有以 TestXXX 開頭的函數。測試框架會運行每個這樣的函數,若是函數調用了一個失敗的函數,如 t.Errort.Error, 那麼測試就算不經過。

經過添加一個測試文件到 stringutil 包中,

$ vim $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 github.com/user/stringutil 目錄中,則直接執行:

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

更多細節能夠運行 go run test 和 查看 測試包文檔

遠程包

導入路徑能夠描述如何使用諸如Git之類的版本控制系統來獲取包源代碼。go工具使用此屬性自動從遠程倉庫獲取包。例如,本文檔中描述的示例也保存在GitHub github.com/golang/example 上託管的Git倉庫中。若是你在包的導入路徑中包含倉庫URL,那麼go將自動獲取,構建和安裝它:

$ 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
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包可供其餘人使用的最簡單方法。

Go Wikigodoc.org提供了外部Go項目的列表。

有關使用go工具使用遠程倉庫的更多信息, 能夠查看 go help importpath

原文地址: https://phpcasts.org/topics/47
相關文章
相關標籤/搜索