如何正確的開始用 Go 編程

本文會演示簡單的 Go 軟件包的開發過程,並介紹了 go 命令行工具,這是我們獲取,構建和安裝 Go 軟件包和命令的標準方法。

go 工具要求你以特定方式組織代碼。我們會介紹 Go 安裝啓動和運行的最簡單方法,一定要仔細閱讀啊。

組織代碼結構

概要

  • Go 程序員一般會將他們的源代碼存放在一個工作區中(多個項目放在一個工作區)
  • 工作區中包含許多由 git 管理的代碼倉庫(也可以是其他版本控制工具管理的)
  • 每個代碼倉庫包含一個或者多個 Go package
  • 每個 package 由單一目錄下的一個或多個 Go 源碼文件組成
  • package 的目錄路徑決定了其導入路徑

與其他編程語言不同的是,在其他編程語言裏每個項目都有自己的工作區,並且工作區都與版本控制系統緊密相關。

工作區

工作區是一個目錄層級,這個目錄層級在頂層有兩個目錄:

  1. src 目錄,存放源代碼文件。

  2. bin 目錄,存放可執行二進制文件。

go 命令工具會把 src 中的 Go 文件構建生成二進制文件放在 bin 目錄中。

src 子目錄通常包含用 git 管理的多個代碼倉庫,他們對應一個或多個 Go 包的開發源碼。

一個典型的工作區中會包含多個源碼倉庫,對應多個可執行命令源碼和包源碼。大多數 Go 程序員會把他們的 Go 源碼和所有依賴的包都放在單一的工作區中。

下面的例子可以讓你更好的瞭解 Go 的工作區大概的樣子:

bin/
    hello                          # 可執行命令文件
    outyet                        # 可執行命令文件
src/
    github.com/golang/example/
          .git/                      
          hello/
              hello.go               # 命令文件源碼
          outyet/
              main.go                # 命令文件源碼
              main_test.go           # 測試文件
          stringutil/
              reverse.go             # package源碼
              reverse_test.go        # 測試文件
    golang.org/x/image/
        .git/               
        bmp/
            reader.go              # package 源碼
            writer.go               # package 源碼
  ......

上面的目錄樹展示了工作區中的兩個代碼倉庫(example 和 image)。example 倉庫中包含兩個命令 hello 和 outyet(hello 和 outyet 目錄中存放的就是兩個命令的源碼)一個被用作庫的 package - stirngutil 。image 倉庫中包含一個 bmp 包。

注意:不能使用符號鏈接(軟鏈 ln -s)將文件鏈接到工作區中。

執行命令和庫是從不同類的源碼包構建出來的,這個之後的部分會進行說明。

GOPATH 環境變量

GOPATH 環境變量指定工作區的位置。它缺省爲用戶目錄中名爲 go 的目錄,因此在 Linux 上爲 $HOME/go,在 Windows 上通常爲 C:\Users\YourName\Go

如果想在其他位置放置工作區,則需要將 GOPATH 設置爲該目錄的路徑。請注意,GOPATH 不得與 GO 安裝路徑相同。

命令 go env GOPATH 打印當前有效的 GOPATH;如果環境變量未設置,它將打印默認位置。爲方便起見,可以請將工作區的 bin 子目錄添加到系統環境變量 $PATH 中

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

同時也把 GOPATH 設置成系統的環境變量:

$ export GOPATH=$(go env GOPATH)

包的導入路徑

一個導入路徑是用來唯一標識包的字符串,包的導入路徑和他在工作區中的位置相對應。標準庫中的包具有較短的導入路徑,如 “fmt” 和 “net/http”。對於您自己的軟件包,你必須選擇一個不太可能與將來添加到標準庫或其他外部庫中的內容衝突的基本路徑。

如果你將代碼保存在某個源代碼庫中,那麼應該使用該源代碼庫的根目錄作爲你的基本路徑。例如,如果你在 github.com 上有一個 GitHub 帳戶 user,你創建的倉庫都會以 github.com/user 爲前綴,那麼 github.com/user 這應該是你的基本路徑。

請注意,在構建代碼之前,你不需要將代碼發佈到遠程存儲庫。就像有一天會發布代碼一樣來組織代碼,這是一個好習慣。實際上,您可以選擇任意路徑名,只要它是唯一的。

我們將使用 github.com/user 作爲基本路徑。在工作區內創建一個保存源代碼的目錄:

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

你的第一個 Go 程序

要編譯並運行一個簡單的程序,首先選擇一個軟件包路徑 (我們將使用 github.com/user/hello),並在您的工作區內創建一個相應的軟件包目錄:

$ mkdir $GOPATH/src/github.com/user/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

go install 構建 hello 命令,生成一個可執行的二進制文件。然後,它將該二進制文件作爲 hello (在 Windows 下爲 hello.exe) 安裝到工作區的 bin 目錄中,hello 可執行命令的位置爲 $GOPATH/bin/hello

Go 工具僅在發生錯誤時打印輸出,因此如果這些命令沒有產生輸出,則代表它們已成功執行。

現在,你可以通過在命令行中鍵入程序的完整路徑來運行該程序:

$ $GOPATH/bin/hello
Hello, world.

由於您已將 $GOPATH/bin 添加到路徑中,因此只需鍵入二進制文件的名字:

$ hello
Hello, world.

你的第一個 library

讓我們編寫一個庫並在上面寫的 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 程序後運行他,可以看到輸出中的字符串已經被反轉了。

$ hello
Hello, Go!

經過上面幾步後你的工作區現在應該看起來像下面這樣:

bin/
    hello                 
src/
    github.com/user/
        hello/
            hello.go      
        stringutil/
            reverse.go    

包名

go 源碼文件中的第一行語句必須是:

package name

其中,name 是用於導入的包的默認名稱。(包中的所有文件必須使用相同的名稱)

go 的慣例是包名是導入路徑的最後一個元素:作爲 “crypto/rot13” 導入的包它的包名爲 rot13

生成可執行命令的源碼文件必須以 main 作爲包名。

go 中不要求鏈接到單個二進制文件的所有包的包名都是唯一的,只要求導入路徑 (它們的完整文件名) 是唯一的。

測試

go 有一個由 go 測試命令和測試包組成的輕量級測試框架。你可以通過創建一個名字以_test.go 結尾的文件來編寫測試,該文件包含名爲 TestXXX 的函數,簽名函數爲 func(t*testing.T)。測試框架運行每個這樣的函數;如果函數調用失敗函數,如 t.Error 或 t.Fail,則認爲測試失敗。

通過創建包含以下 go 代碼的文件 $GOPATH/src/github.com/user/stringutil/reverse_test.go,將測試添加到 strangutil 包。

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

導入路徑可以描述如何從版本控制系統 (如 Git) 獲取包源代碼。Go 工具使用此屬性自動從遠程倉庫中獲取包。例如,本文檔中描述的示例也保存在 GitHub 以 github.com/golang/example 託管的 Git 存儲庫中。如果將代碼倉庫的 URL 包含在軟件包的導入路徑中,go 將會使用 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                         
src/
    github.com/golang/example/
        .git/                    
        hello/
            hello.go           
        stringutil/
            reverse.go        
            reverse_test.go     
    github.com/user/
        hello/
            hello.go           
        stringutil/
            reverse.go        
            reverse_test.go   

託管在 GitHub 上的 hello 命令依賴於同一倉庫中的 stringutil 包。hello.go 文件中的導入使用相同的導入路徑約定,因此 go get 命令也能夠定位和安裝依賴包。

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

What's Next

發佈了41 篇原創文章 · 獲贊 727 · 訪問量 459萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章