Go 1.20要來了,看看都有哪些變化-第2篇

coding進階發表於2022-12-21

前言

Go官方團隊在2022.12.08釋出了Go 1.20 rc1(release candidate)版本,Go 1.20的正式release版本預計會在2023年2月份釋出。

讓我們先睹為快,看看Go 1.20給我們帶來了哪些變化。

安裝方法:

$ go install golang.org/dl/go1.20rc1@latest
$ go1.20rc1 download

這是Go 1.20版本更新內容詳解的第2篇,歡迎大家關注公眾號,及時獲取本系列最新更新。

第1篇主要涉及Go 1.20在語言、可移植性方面的最佳化,原文連結:Go 1.20版本升級內容第1篇

Go 1.20釋出清單

和Go 1.19相比,改動內容適中,主要涉及語言(Language)、可移植性(Ports)、工具鏈(Go Tools)、執行時(Runtime)、編譯器(Compiler)、彙編器(Assembler)、連結器(Linker)和核心庫(Core library)等方面的最佳化。

本文重點介紹Go 1.20在Go工具鏈方面的最佳化。

Go command

$GOROOT/pkg路徑不再儲存標準庫原始碼編譯後生成的檔案,包括以下幾點:

  • go install不再往$GOROOT/pkg目錄寫檔案。
  • go build不再檢查$GOROOT/pkg下的檔案。
  • Go釋出包不再帶有這些編譯檔案。

以macOS環境來演示:在Go 1.16版本里,$GOROOT/pkg目錄下的內容如下:

$ go version
go version go1.16.5 darwin/amd64
$ go env GOROOT
/usr/local/opt/go/libexec
$ ls /usr/local/opt/go/libexec/pkg
darwin_amd64        darwin_amd64_race    include            tool

但是在Go 1.20rc1版本里,$GOROOT/pkg目錄下的內容如下:

$ go1.20rc1 version
go version go1.20rc1 darwin/amd64
$ go1.20rc1 env GOROOT
/Users/xxx/sdk/go1.20rc1
$ ls /Users/xxx/sdk/go1.20rc1/pkg
include    tool

少了darwin_amd64darwin_amd64_race這2個資料夾:

$ ls /usr/local/opt/go/libexec/pkg/darwin_amd64
archive        database    go        io        net.a        runtime        testing.a
bufio.a        debug        hash        io.a        os        runtime.a    text
bytes.a        embed.a        hash.a        log        os.a        sort.a        time
cmd        encoding    html        log.a        path        strconv.a    time.a
compress    encoding.a    html.a        math        path.a        strings.a    unicode
container    errors.a    image        math.a        plugin.a    sync        unicode.a
context.a    expvar.a    image.a        mime        reflect.a    sync.a        vendor
crypto        flag.a        index        mime.a        regexp        syscall.a
crypto.a    fmt.a        internal    net        regexp.a    testing
$ ls /usr/local/opt/go/libexec/pkg/darwin_amd64_race/
archive        debug        hash        io.a        os        runtime.a    text
bufio.a        embed.a        hash.a        log        os.a        sort.a        time
bytes.a        encoding    html        log.a        path        strconv.a    time.a
compress    encoding.a    html.a        math        path.a        strings.a    unicode
container    errors.a    image        math.a        plugin.a    sync        unicode.a
context.a    expvar.a    image.a        mime        reflect.a    sync.a        vendor
crypto        flag.a        index        mime.a        regexp        syscall.a
crypto.a    fmt.a        internal    net        regexp.a    testing
database    go        io        net.a        runtime        testing.a

從Go 1.20開始,標準庫的package會按需編譯,編譯後生成的檔案會快取在編譯快取(build cache)裡,就像非GOROOT下的package一樣。這個修改減小了Go安裝包的大小。

go test -json的實現在Go 1.20版本更加魯棒,對使用go test -json的開發者來說,不用做任何改變。

但是,直接呼叫Go工具test2json的開發者,需要給測試的可執行程式增加-v=test2json引數,例如go test -v=test2json或者./pkg.test -test.v=test2json,而不是僅僅加一個-v標記。

go test -json的另外一個修改是在每個測試程式執行的開始,增加了一個Action事件。當使用go命令同時執行多個測試程式時,這些Action事件的執行會按照命令列裡的package的順序按序執行。

go命令現在新增了和CPU架構相關的編譯標記引數,例如amd64.v2。有了這個引數後,在業務程式碼實現的時候就可以根據CPU架構的不同而做不同的處理。更多細節可以參考:go help buildconstraint

go子命令現在支援-C <dir>引數,可以在執行命令前改變目錄到<dir>下。如果一個指令碼要在多個不同的Go module下執行,這個特性會帶來方便。

go buildgo test 命令不再支援-i引數,這個引數從 Go 1.16版本開始棄用

go generate 命令接受 -skip <pattern> 引數,可以跳過匹配 <pattern>格式的 //go:generate 指令。

go test命令接受-skip <pattern> 引數,可以跳過匹配 <pattern>格式的測試用例。

go build, go install和其它編譯相關的命令新增了一個-pgo標記引數,可以輔助開發者做程式最佳化。-pgo指定的是profile檔案的路徑。如果-pgo=auto,那go命令會在main這個包的路徑下去找名為default.pgo的檔案。-pgo=off可以關閉最佳化。詳情可以參考:PGO Proposal

go build, go install和其他編譯相關的命令新增了一個-cover標記引數,可以用來對編譯出來的可執行程式做程式碼覆蓋率收集,詳情可以參考本文後面介紹。

go version

go version -m命令支援讀取和解析更多型別的Go二進位制檔案。

比如透過go build -buildmode=c-share編譯出來的Windows DLL檔案以及沒有可執行許可權的Linux二進位制檔案,現在都可以被go version -m解析和識別到。

Cgo

早期Go語言的一些標準庫是用C語言實現的,需要依賴cgo來作為go語言和C語言的橋樑。

現在Go語言開始去除對C語言的的依賴。

從Go 1.20版本開始,如果機器上沒有C語言的工具鏈,go命令會預設禁用cgo

具體而言:如果沒有設定CGO_ENABLEDCC環境變數,而且預設的C語言編譯器(例如clanggcc)也找不到,那CGO_ENABLED會預設為0。當然開發者可以透過設定CGO_ENABLED環境變數的值來改變CGO_ENABLED的值。

這個修改會讓Go語言減少對C語言工具鏈的依賴,適配更多的環境,尤其是最小化的容器環境以及macOS環境。

Go標準庫裡使用了cgo的package有: net, os/userplugin

在macOS環境,netos/user包已經被重寫了,不再依賴cgo。現在netos/user的程式碼實現既可以用cgo編譯,也可以不用cgo編譯。同時,在macOS環境,race detector已經被重寫了,不再依賴cgo。

在Windows環境,netos/user沒有使用過cgo。

在其它作業系統上,如果編譯的時候禁用了cgo,那會使用這些包的純go語言實現。

在macOS環境,race detector已經被重寫了,不再依賴cgo。

Cover(程式碼覆蓋率檢測)

Go 1.20版本之前只支援對單元測試(unit test)收集程式碼覆蓋率,從Go 1.20版本開始支援對任何Go程式做程式碼覆蓋率收集。

那如何收集呢?需要做如下操作:

  • go build編譯命令增加-cover標記
  • 給環境變數GOCOVERDIR賦值為某個路徑
  • 執行go build編譯出來的可執行程式時,會把程式碼覆蓋率檔案輸出到GOCOVERDIR指定的路徑下。

詳細的介紹文件和使用說明可以參考: coverage for integration tests' landing page

想了解設計原理和實現的可以參考: proposal

Vet

檢測迴圈變數被巢狀子函式錯誤使用的場景

func TestTLog(t *testing.T) {
    t.Parallel()
    tests := []struct {
        name  string
        value int
    }{
        {name: "test 1", value: 1},
        {name: "test 2", value: 2},
        {name: "test 3", value: 3},
        {name: "test 4", value: 4},
    }
    for _, tc := range tests {
        t.Run(tc.name, func(t *testing.T) {
      t.Parallel()
            // Here you test tc.value against a test function.
            // Let's use t.Log as our test function :-)
            t.Log(tc.value)
        })
    }
}

大家可以猜一下這段程式裡t.Log列印的結果是什麼?結果是為1,2,3,4還是4,4,4,4?是否和自己的預期相符。

想了解詳情的可以參考:Be Careful with Table Driven Tests and t.Parallel()

這個本質上和goroutine與閉包函式一起使用時,遇到的迴圈變數問題一樣。

Go 1.20版本開始透過go vet可以檢測出單元測試裡的這類問題。

檢查錯誤時間格式

對於 Time.Formattime.Parse,如果程式碼裡要轉成yyyy-dd-mm的格式,會給出提示。

因為yyyy-dd-mm不符合常用的日期格式標準,ISO 8601日期格式是yyyy-mm-dd格式。

總結

下一篇會介紹Go 1.20在執行時、編譯器、彙編器、連結器和核心庫的最佳化工作,有一些內容值得學習,歡迎大家保持關注。

推薦閱讀

開源地址

文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程

公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。

個人網站:Jincheng's Blog

知乎:無忌

福利

我為大家整理了一份後端開發學習資料禮包,包含程式語言入門到進階知識(Go、C++、Python)、後端開發技術棧、面試題等。

關注公眾號「coding進階」,傳送訊息 backend 領取資料禮包,這份資料會不定期更新,加入我覺得有價值的資料。還可以傳送訊息「進群」,和同行一起交流學習,答疑解惑。

References

相關文章