初學Golang的筆記

HugeYuan 發表於 2021-05-09
Go

Note

  1. 一個module是一個go package的集合,該module用於釋出的一個單位。

  2. 一般一個go repo僅僅包含一個module,含有一個go.mod檔案

  3. 每個module路徑不僅僅用於作為import的字首,也用於幫助go command來查詢下載這個module,比如我們import module golang.org/x/tools,go command 還需要從https://golang.org/x/tools 這個地方下載這個module。(詳情見:https://golang.org/cmd/go/#hdr-Relative_import_paths)

go command在定位一個給定module的repo的時候,會通過https請求並且讀取HTML內嵌的後設資料,(詳情見:https://golang.org/cmd/go/#hdr-Remote_import_paths),很多主句服務已經提供了go code的repo後設資料資訊,所以最佳設定你的module的方法通常是將你的module名和module repo URL路徑相匹配,這樣別人能夠根據module名就可以找到你的repo具體在哪。

  1. 每import一個包路徑,這個路徑以及其所有子路徑如果有其他包的話,都將不被包含。
    比如:引入包github.com/google/go-cmp,則go-cmp/裡面的cmp/.,如果想要引入cmp的包那麼就需要包含github.com/google/go-cmp/cmp這個包,所包含的包沒有module字首包含關係。

  2. 第一次開始建立module

$ mkdir hello # Alternatively, clone it if it already exists in version control.
$ cd hello
$ go mod init example.com/user/hello
go: creating new go.mod: module example.com/user/hello
$ cat go.mod
module example.com/user/hello

go 1.16

接著開始寫go原始檔,第一行是“包名”

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}

這樣就有了這個module的包以及內容,以及你宣告的路徑。
然後開始build 和install go tool:

go install example.com/user/hello

這個命令先編譯成一個二進位制檔案,然後安裝到你的PATH路徑中存bin的檔案目錄中(這個具體存在哪可以通過GOPATH 和GOBIN這些go env環境變數來定)。如果GOBIN設定的話,那麼就按照GOBIN設定的地方存放這個binary。

go env -w GOBIN=/somewhere/else/bin

如果取消設定環境變數可以通過 go env -w, 或者go env -u:

go env -u GOBIN

例項演示:自己編譯一個module,然後本地import並使用

  1. 先自己code一個包,假如這個包在本地$HOME/hello/morestrings,然後裡面有一個function
func ReverseRunes(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)
}

注意:function首字母要大寫,只有首字母大寫的function才是exported.(https://golang.org/ref/spec#Exported_identifiers)
2. 然後build這個module

go build
  1. 在另外一個code裡面去使用這個module
package main

import (
	"fmt"

	"example.com/user/hello/morestrings"
)
func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

然後安裝引用的包,最後執行。

go install example.com/user/hello
hello
Hello, Go!

例項演示:引用一個遠端的module
現在假如我們想要引用一個遠端的module,如果這個module是通過版本控制系統比如“Git, Mercurial”等控制,go tool會自動根據路徑下載並使用這些repo,比如基於上個例子我們想使用一個遠端庫github.com/google/go-cmp/cmp 到你的專案裡:

package main

import (
	"fmt"

	"example.com/user/hello/morestrings"
	"github.com/google/go-cmp/cmp"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
	fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

現在你的專案依賴"github.com/google/go-cmp/cmp"這個包,那麼你就需要下載這個包,同時你要在go.mod上面記錄你想引用這個包的版本。
go mod tidy 命令可以自動下載你引用但是沒有下載的包,同時去掉那些不在使用的。

go mod tidy

下載的包在指定的require版本其他所有版本中都是共享的,也因此go command把這些儲存下載好的包的檔案以及資料夾設定為只讀的,防止使用者更改下載下來的應用包。
如果想移除所有下載好的包,可以通過
go clean -modcache

例項演示:testing 包(詳情見https://golang.org/cmd/go/#hdr-Test_packages testing 包doc:https://golang.org/pkg/testing/)

  1. testing包的測試檔案可以通過go test命令來測試

  2. 你寫的go 程式碼
    以 _test.go結尾的;
    函式名是Test***
    並且有 func(t *testing.T)為函式簽名的function
    全部被視為test程式碼。

  3. testfunction中如果呼叫t.Error or t.Fail,這時候就認為測試失敗,現在看看下面測試ReverseRunes的例子。
    注意:
    檔名:$HOME/hello/morestrings/reverse_test.go
    函式簽名:func TestReverseRunes(t *testing.T)

package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"Hello, world", "dlrow ,olleH"},
		{"Hello, 世界", "界世 ,olleH"},
		{"", ""},
	}
	for _, c := range cases {
		got := ReverseRunes(c.in)
		if got != c.want {
			t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

然後執行test程式碼:

go test
PASS
ok  	example.com/user/morestrings 0.165s