前言
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_amd64
和darwin_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
build
和 go
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_ENABLED
和CC
環境變數,而且預設的C語言編譯器(例如clang
和gcc
)也找不到,那CGO_ENABLED
會預設為0。當然開發者可以透過設定CGO_ENABLED
環境變數的值來改變CGO_ENABLED
的值。
這個修改會讓Go語言減少對C語言工具鏈的依賴,適配更多的環境,尤其是最小化的容器環境以及macOS環境。
Go標準庫裡使用了cgo的package有: net
, os/user
和 plugin
。
在macOS環境,net
和os/user
包已經被重寫了,不再依賴cgo。現在net
和os/user
的程式碼實現既可以用cgo編譯,也可以不用cgo編譯。同時,在macOS環境,race detector已經被重寫了,不再依賴cgo。
在Windows環境,net
和os/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.Format
和time.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 領取資料禮包,這份資料會不定期更新,加入我覺得有價值的資料。還可以傳送訊息「進群」,和同行一起交流學習,答疑解惑。