8 千字詳解 Go1-20 穩定版

Go1.20 變化不少,該版本依然保持 Go1 兼容性,我們可以升級到 Go1.20,而不需要做任何代碼改動。

可以使用你任何喜歡的方式升級:

比如:go install golang.org/dl/go1.20@latest

具體的可以參考官網教程:https://go.dev/doc/go1.20

Go 1.20 簡介

最新的 Go 版本 1.20 在 Go 1.19 發佈六個月後發佈。它的大部分更改都在工具鏈、運行時和庫的實現中。

一如既往,該版本保持了 Go 1 的兼容性承諾。我們期望幾乎所有的 Go 程序都能像以前一樣繼續編譯和運行。

語言的變化

Go 1.20 包括對語言的四個更改。

Go 1.17 添加了從切片到數組指針的轉換。Go 1.20 擴展了它以允許從切片到數組的轉換:給定一個切片x,[4]byte(x)現在可以寫成*(*[4]byte)(x).

unsafe包定義了三個新函數SliceDataStringStringData。與 Go 1.17 一起Slice,這些函數現在提供了構建和解構切片和字符串值的完整能力,而不依賴於它們的確切表示。

該規範現在定義結構值一次比較一個字段,按照它們在結構類型定義中出現的順序考慮字段,並在第一個不匹配時停止。之前可以閱讀規範,就好像所有字段都需要比較第一個不匹配之外的字段。類似地,該規範現在定義數組值按遞增索引順序一次比較一個元素。在這兩種情況下,差異會影響某些比較是否必須恐慌。現有程序沒有改變:新的規範措辭描述了實現一直所做的事情。

可比較的類型(例如普通接口)現在可以滿足comparable約束,即使類型參數不是嚴格可比較的(比較可能會在運行時崩潰)。這使得實例化受約束的類型參數comparable (例如,用戶定義的通用映射鍵的類型參數)與非嚴格可比較的類型參數(例如接口類型或包含接口類型的複合類型)成爲可能。

端口

Windows

Go 1.20 是將在 Windows 7、8、Server 2008 和 Server 2012 的任何版本上運行的最後一個版本。Go 1.21 將至少需要 Windows 10 或 Server 2016。

Darwin and iOS

Go 1.20 是將在 macOS 10.13 High Sierra 或 10.14 Mojave 上運行的最後一個版本。Go 1.21 將需要 macOS 10.15 Catalina 或更高版本。

FreeBSD/RISC-V

GOOS=freebsdGo 1.20 在 RISC-V ( , GOARCH=riscv64) 上添加了對 FreeBSD 的實驗性支持。

工具

Go command

該目錄$GOROOT/pkg不再存儲標準庫的預編譯包存檔: go install不再寫入它們,go構建不再檢查它們,Go 發行版不再運送它們。相反,標準庫中的包是根據需要構建的,並緩存在構建緩存中,就像外部的包一樣GOROOT。此更改減少了 Go 發行版的大小,還避免了使用 cgo 的包的 C 工具鏈傾斜。

實施go test -json 已得到改進,使其更加健壯。運行的程序go test -json 不需要任何更新。直接調用的程序現在應該使用 (例如, 或)而不是普通的來go tool test2json 運行測試二進制文件。 -v=test2json``go test -v=test2json``./pkg.test -test.v=test2json``-v

一個相關的變化是 在每個測試程序開始執行時go test -json 添加了一個Action設置爲的事件。start當使用命令運行多個測試時go,這些啓動事件保證以與命令行中指定的包相同的順序發出。

go命令現在定義了體系結構功能構建標籤,例如amd64.v2,以允許根據特定體系結構功能的存在或不存在來選擇包實現文件。詳情請見go help buildconstraint

go子命令現在接受 在執行命令之前 將-C <dir>目錄更改爲 <dir>,這對於需要在多個不同模塊中執行命令的腳本可能很有用。

go buildandgo test 命令不再接受-i標誌,該標誌自 Go 1.16 以來已 被棄用。

go generate命令現在接受 -skip <pattern>跳過//go:generate匹配的指令<pattern>

go test命令現在接受 -skip <pattern>跳過測試、子測試或匹配的示例<pattern>

當主模塊位於 中GOPATH/src時, go install不再爲非main包安裝庫GOPATH/pkg,並且go list不再報告Target 此類包的字段。(在模塊模式下,已編譯的包僅存儲在 構建緩存中 ,但一個錯誤導致GOPATH安裝目標意外地保持有效。)

go buildgo install其他與構建相關的命令現在支持-pgo啓用配置文件引導優化的標誌,這在下面的 編譯器部分中有更詳細的描述。該-pgo標誌指定配置文件的文件路徑。指定-pgo=auto會導致go命令搜索default.pgo在主包目錄中命名的文件,如果存在則使用它。此模式目前需要在命令行上指定一個主包,但我們計劃在未來的版本中取消此限制。指定-pgo=off關閉配置文件引導的優化。

go buildgo install其他與構建相關的命令現在支持-cover 使用代碼覆蓋檢測構建指定目標的標誌。這在下面的封面部分 中有更詳細的描述 。

go version

go version -m命令現在支持讀取更多類型的 Go 二進制文件,最值得注意的是,使用構建的 Windows DLLgo build -buildmode=c-shared 和沒有執行權限的 Linux 二進制文件。

CGO

go命令現在默認在沒有 C 工具鏈的系統上禁用cgo。更具體地說,當CGO_ENABLED環境變量未設置時,環境變量未設置,並且在路徑中找不到 CC默認的 C 編譯器(通常是clang或),默認爲. 與往常一樣,您可以通過顯式設置來覆蓋默認值。 gcc``CGO_ENABLED``0``CGO_ENABLED

默認更改最重要的影響是,當 Go 安裝在沒有 C 編譯器的系統上時,它現在將使用純 Go 構建標準庫中使用 cgo 的包,而不是使用預分發的包存檔(有已被刪除,如上所述)或嘗試使用 cgo 並失敗。這使得 Go 在一些最小的容器環境以及 macOS 上工作得更好,在 macOS 上,自 Go 1.16 以來,預分發的包存檔還沒有用於基於 cgo 的包。

標準庫中使用 cgo 的包有net、 os/user和 plugin。在 macOS 上,netos/user包已被重寫爲不使用 cgo:相同的代碼現在用於 cgo 和非 cgo 構建以及交叉編譯的構建。在 Windows 上,netos/user包從未使用過 cgo。在其他系統上,禁用 cgo 的構建將使用這些包的純 Go 版本。

在 macOS 上,競態檢測器已被重寫爲不使用 cgo:啓用競態檢測器的程序可以在沒有 Xcode 的情況下構建和運行。在 Linux 和其他 Unix 系統以及 Windows 上,需要主機 C 工具鏈才能使用競爭檢測器。

Cover

Go 1.20 支持收集程序(應用程序和集成測試)的代碼覆蓋率配置文件,而不僅僅是單元測試。

要收集程序的覆蓋率數據,請使用go build-cover標誌構建它,然後運行生成的二進制文件,並將環境變量GOCOVERDIR設置爲覆蓋率配置文件的輸出目錄。有關如何開始的更多信息,請參閱 “集成測試覆蓋率” 登錄頁面。詳細設計和實現見 提案。

Vet

改進了嵌套函數對循環變量捕獲的檢測

該工具現在報告 在子測試函數體內vet調用後對循環變量的引用。T.Parallel()此類引用可能會觀察來自不同迭代的變量值(通常會導致測試用例被跳過)或由於不同步的併發訪問而導致的無效狀態。

該工具還在更多地方檢測引用錯誤。以前它只會考慮循環體的最後一條語句,但現在它遞歸地檢查 if、switch 和 select 語句中的最後一條語句。

針對錯誤時間格式的新診斷

vet 工具現在報告使用時間格式 2006-02-01 (yyyy-dd-mm)Time.Format和 time.Parse。此格式未出現在通用日期標準中,但在嘗試使用 ISO 8601 日期格式 (yyyy-mm-dd) 時經常被錯誤使用。

Runtime

一些垃圾收集器的內部數據結構被重新組織,以提高空間和 CPU 效率。此更改減少了內存開銷並將整體 CPU 性能提高了 2%。

在某些情況下,垃圾收集器在 goroutine 協助方面表現得不太不穩定。

Go 1.20 添加了一個runtime/coverage包含 API 的新包,用於在運行時從長時間運行和 / 或不通過os.Exit().

編譯器 Compiler

Go 1.20 添加了對配置文件引導優化 (PGO) 的預覽支持。PGO 使工具鏈能夠根據運行時配置文件信息執行特定於應用程序和工作負載的優化。目前,編譯器支持 pprof CPU 配置文件,可以通過常規方式收集,例如runtime/pprof或 net/http/pprof包。要啓用 PGO,請通過 -pgo標誌將 pprof 配置文件的路徑傳遞給go build,如上所述。Go 1.20 使用 PGO 更積極地在熱調用站點內聯函數。一組具有代表性的 Go 程序的基準顯示啓用配置文件引導的內聯優化可將性能提高約 3–4%。請參閱 PGO 用戶指南獲取詳細文檔。我們計劃在未來的版本中添加更多配置文件引導的優化。請注意,配置文件引導的優化是一個預覽,因此請謹慎使用。

Go 1.20 編譯器升級了它的前端以使用一種新的方式來處理編譯器的內部數據,它修復了幾個泛型類型問題並在泛型函數和方法中啓用了類型聲明。

編譯器現在 默認拒絕帶有編譯器錯誤的匿名接口循環。這些源於嵌入式接口的巧妙使用, 並且一直存在細微的正確性問題,但我們沒有證據表明它們確實在實踐中使用過。假設沒有用戶報告受到此更改的不利影響,我們計劃更新 Go 1.22 的語言規範以正式禁止它們,以便工具作者也可以停止支持它們。

Go 1.18 和 1.19 的構建速度有所下降,這主要是由於增加了對泛型的支持和後續工作。Go 1.20 將構建速度提高了 10%,使其與 Go 1.17 保持一致。相對於 Go 1.19,生成的代碼性能也普遍略有提升。

鏈接器 Linker

glibc 在 Linux 上,鏈接器現在爲鏈接時或musl在鏈接時 選擇動態解釋器。

在 Windows 上,Go 鏈接器現在支持現代的基於 LLVM 的 C 工具鏈。

Go 1.20 對編譯器生成的符號使用go:andtype:前綴,而不是go.and type.。這避免了名稱以 . 開頭的用戶包的混淆go.。該debug/gosym軟件包理解使用 Go 1.20 及更新版本構建的二進制文件的新命名約定。

引導程序 Bootstrap

當從源代碼構建 Go 版本GOROOT_BOOTSTRAP且未設置時,以前版本的 Go 在目錄中查找 Go 1.4 或更高版本的引導工具鏈 $HOME/go1.4%HOMEDRIVE%%HOMEPATH%\go1.4在 Windows 上)。Go 1.18 和 Go 1.19 在回退到 之前首先尋找$HOME/go1.17或,以預期在引導 Go 1.20 時需要使用 Go 1.17。Go 1.20 確實需要 Go 1.17 版本來進行引導,但我們意識到我們應該採用引導工具鏈的最新點版本,因此它需要 Go 1.17.13。Go 1.20 尋找或 回退到之前$HOME/sdk/go1.17``$HOME/go1.4``$HOME/go1.17.13``$HOME/sdk/go1.17.13``$HOME/go1.4 (以支持硬編碼路徑 $HOME/go1.4 但在那裏安裝了更新的 Go 工具鏈的系統)。未來,我們計劃大約每年將引導工具鏈向前移動一次,特別是我們預計 Go 1.22 將需要 Go 1.20 的最終版本來進行引導。

核心庫

New crypto/ecdh package

Go 1.20 添加了一個新crypto/ecdh包,以明確支持 NIST 曲線和 Curve25519 上的橢圓曲線 Diffie-Hellman 密鑰交換。

程序應該爲 ECDH 使用crypto/ecdh而不是低級功能 crypto/elliptic,而爲更高級的用例使用第三方模塊。

包裝多個錯誤

Go 1.20 擴展了對錯誤包裝的支持,允許一個錯誤包裝多個其他錯誤。

一個錯誤e可以通過提供一個Unwrap返回[]error.

和函數已更新以檢查多重包裝錯誤 errors.Is。 errors.As

fmt.Errorf函數現在支持多次出現%w格式動詞,這將導致它返回一個包含所有這些錯誤操作數的錯誤。

新函數errors.Join 返回一個包含錯誤列表的錯誤。

HTTP 響應控制器

新 "net/http".ResponseController 類型提供對接口未處理的擴展的按請求功能的 "net/http".ResponseWriter訪問。

以前,我們通過定義ResponseWriter可以實現的可選接口(例如 Flusher. 這些接口不可發現且使用起來很笨拙。

ResponseController類型提供了一種更清晰、更易於發現的方式來添加每個處理程序的控件。Go 1.20 中還添加了兩個這樣的控件是 SetReadDeadlineSetWriteDeadline,它們允許設置每個請求的讀寫截止日期。例如:

func RequestHandler(w ResponseWriter, r *Request) {
  rc := http.NewResponseController(w)
  rc.SetWriteDeadline(time.Time{}) // 發送大響應時禁用 Server.WriteTimeout
  io.Copy(w, 大數據)
}

新的 ReverseProxy 重寫 hook

httputil.ReverseProxy 轉發代理包括一個新的 鉤子Rewrite 函數,取代了以前的Director鉤子。

Rewrite掛鉤接受一個 ProxyRequest參數,該參數包括代理接收的入站請求和它將發送的出站請求。與Director僅對出站請求進行操作的掛鉤不同,這允許Rewrite掛鉤避免某些情況,在這些情況下,惡意入站請求可能導致掛鉤添加的標頭在轉發之前被刪除。請參閱問題 #50580。

ProxyRequest.SetURL 方法將出站請求路由到提供的目的地並取代該NewSingleHostReverseProxy功能。與 不同的NewSingleHostReverseProxy是,SetURL 還設置了Host出站請求的標頭。

該 ProxyRequest.SetXForwarded 方法設置出站請求的X-Forwarded-ForX-Forwarded-HostX-Forwarded-Proto標頭。使用 aRewrite時,默認情況下不會添加這些標頭。

Rewrite使用這些功能的掛鉤 示例是:

proxyHandler := &httputil.ReverseProxy{
  重寫:func(r *httputil.ProxyRequest) {
    r.SetURL(outboundURL) // 轉發請求到 outboundURL。
    r.SetXForwarded() // 設置 X-Forwarded-* 標頭。
    r.Out.Header.Set("X-Additional-Header""代理設置的header")
  },
}

ReverseProxy``User-Agent當傳入請求沒有時, 不再向轉發的請求添加標頭。

library 的小改動

與往常一樣,庫有各種小的變化和更新,考慮到 Go 1 的兼容性承諾 。還有各種性能提升,這裏就不一一列舉了。

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Se7VQaaS66IbR6kAQxRJAw