Go 1.22 發行說明

Go 1.22 簡介

最新的 Go 版本 1.22 在Go 1.21推出六個月後登場。大部份的變更都實作於工具鏈、執行時期和函式庫中。如同以往,此版本維護著 Go 1 的相容性承諾。我們預期幾乎所有 Go 程式都可以持續像以前一樣編譯和執行。

語言的變更

Go 1.22 對「for」迴圈做出兩項變更。

Go 1.22 包含我們正在考慮在未來 Go 版本採用的一項語言變更預覽:range-over-function 迭代器。使用 GOEXPERIMENT=rangefunc 建置可啟用這項功能。

工具

Go 指令

工作區 的指令現在可使用包含工作區相依項目的 vendor 目錄。此目錄由 go work vendor 建立,並由建置指令在 -mod 旗標設為 vendor 時使用,而後者是在存在工作區 vendor 目錄時預設值。

請注意,工作區的 vendor 目錄中的內容不同於單一模組:如果工作區根目錄中的目錄也包含工作區中的其中一個模組,其 vendor 目錄可包含工作區或模組的相依項目,但無法同時包含這兩個項目的相依項目。

不再支援舊版 GOPATH 模式(即 GO111MODULE=off)中模組外的 go get。其他建置指令(例如 go buildgo test)將持續無限期地運作於舊版 GOPATH 程式。

go mod init 不再嘗試從其他供應商工具(例如 Gopkg.lock)的設定檔匯入模組需求。

go test -cover 現在會列印沒有自己的測試檔案之已覆蓋套件的覆蓋率摘要。在 Go 1.22 之前,對此類套件執行的 go test -cover 會回報

? mymod/mypack [no test files]

現在在 Go 1.22 中,套件中的函數被視為未覆蓋。

mymod/mypack coverage: 0.0% of statements

請注意,如果套件完全不包含可執行程式碼,我們無法回報有意義的覆蓋率百分比;對於此類套件,go 工具將持續回報沒有測試檔案。

如果將使用外部 (C) 連結器但未啟用 CGO,則呼叫連結器的 go 建置指令現在會傳出錯誤。(Go 執行時期需要 CGO 支援,才能確保與 C 連結器新增的任何其他函式庫相容。)

追蹤

trace 工具的網頁使用者介面在支援新追蹤程式的工作中已溫和的重新整理,解決了幾個問題,並改善了各種子頁面的可讀性。網頁使用者介面現在支援以執行緒為導向的檢視方式瀏覽追蹤。追蹤檢視器現在也會顯示所有系統呼叫的完整持續時間。
這些改進僅適用於檢視由 Go 1.22 或更新版本所建置的程式所產生的追蹤。未來版本將會把部分改善套用於舊版 Go 產生的追蹤。

驗證

循序變數的參考

vet 工具的行為已變更,以符合 Go 1.22 中循環變數的新語意 (見上文)。當分析需要 Go 1.22 或更新版本 (由於其 go.mod 檔案或單一檔案建置限制) 的檔案時,vet 不再通報在可能比循環疊代時間更長的函式字面中的循環變數參考。在 Go 1.22 中,針對各疊代新建立循環變數,因此此類參考不再有在循環更新變數後使用變數的風險。

附加後遺失值的全新警告

vet 工具現在會針對未傳遞要附加到區段的值的 append 呼叫發出通報,例如 slice = append(slice)。這種陳述式沒有作用,且經驗顯示幾乎總是一個錯誤。

遞延 time.Since 的全新警告

vet 工具現在會針對 defer 陳述式中未遞延呼叫 time.Since(t) 的情形發出通報。這相當於在 defer 陳述式之前 (而非在呼叫遞延函式時) 呼叫 time.Now().Sub(t)。在幾乎所有情形中,正確程式碼都要求遞延 time.Since 呼叫。例如

t := time.Now()
defer log.Println(time.Since(t)) // non-deferred call to time.Since
tmp := time.Since(t); defer log.Println(tmp) // equivalent to the previous defer

defer func() {
  log.Println(time.Since(t)) // a correctly deferred call to time.Since
}()

log/slog 呼叫中不配對的鍵值對的全新警告

vet 工具現在會針對結構化記錄套件 log/slog 中函式與方法的呼叫報告無效引數,這些函式與方法會接受交替的鍵/值對。此工具會針對在鍵位置的引數既非 字串 也非 slog.Attr 的呼叫,以及遺失值的最後一個鍵報告錯誤。

執行時期

執行時期現在將基於類型的垃圾回收資訊保留得更靠近各堆疊物件,改善 Go 程式的 CPU 效能 (延遲或處理量) 1-3%。此變更也透過重複資料的備份減少大部份 Go 程式約 1% 的記憶體耗用。有些程式可能看到較小的改善,因為此變更調整記憶體配置器的尺寸類界線,因此有些物件可能會往上移動到一個尺寸類別。

這個變更造成某些物件的位址過去總是對齊至 16 位元組(或更高)的界線,現在僅對齊至 8 位元組界線。這可能會導致部份使用需要 8 位元組以上對齊記憶體位址的組合語言指令的程式中斷,且依賴記憶體配置程式先前的對齊動作,不過我們預期此類程式將相當少見。此類程式可以使用GOEXPERIMENT=noallocheaders 編譯,以還原至舊的資料配置方式並回復先前的對齊動作,但套件所有人應更新其組合語言程式碼以避免對齊假設,因為此暫時解決方案將於未來的版本中移除。

windows/amd64 埠上,連結或載入使用-buildmode=c-archive-buildmode=c-shared 編譯的 Go 函式庫,現在可以使用SetUnhandledExceptionFilter Win32 函數來捕捉 Go 執行階段未處理的例外。請注意,這已在windows/386 埠上受支援。

編譯器

輪廓引導最佳化 (PGO) 編譯現在可以取消更多比例的呼叫的虛擬化,超過過去的可能性。代表性的 Go 程式組中的大多數程式現在因啟用 PGO 而看到的執行時間進步約在 2% 到 14% 之間。

編譯器現在交錯取消虛擬化和內聯,因此介面方法呼叫獲得更好的最佳化。

Go 1.22 也包含編譯器的內聯階段的擴充實作之預覽,其利用啟發法在評為「重要」的呼叫點(例如,在迴圈中)提升內聯性,並在評為「不重要」的呼叫點(例如,在恐慌路徑上)降低內聯性。使用GOEXPERIMENT=newinliner 編譯可啟用新的呼叫點啟發法;參閱問題 #61502以取得更多資訊並提供意見反應。

連結器

連結器的-s-w 旗標現在在所有平台上都表現得更一致。-w 旗標會抑制 DWARF 除錯資訊產生。-s 旗標會抑制符號表產生。-s 旗標也會暗示-w 旗標,而後者可以使用-w=0 否定。亦即,-s -w=0 會產生包含 DWARF 除錯資訊但沒有符號表的二進位檔。

在 ELF 平台上,連結器的-B 旗標現在接受一種特殊形式:搭配-B gobuildid,連結器會從 Go 建置 ID 產生 GNU 建置 ID(ELF NT_GNU_BUILD_ID 標籤)。

在 Windows 上,使用-linkmode=internal 編譯時,連結器現在會從 C 物件檔案封存 SEH 資訊,方法是將.pdata.xdata 區段複製到最終的二進位檔。這有助於使用原生程式,例如 WinDbg,除錯和分析二進位檔。請注意,到目前為止,C 函數的 SEH 例外處理常式未受到重視,因此這個變更可能會導致某些程式的行為有所不同。-linkmode=external 不會受到此變更影響,因為外部連結器已封存 SEH 資訊。

開機

Go 1.20 發行說明 中所述,Go 1.22 對於 bootstrap 現在需要 Go 1.20 或更新版本的最後點亮發行版本。我們預計 Go 1.24 對於 bootstrap 將需要 Go 1.22 或更新版本的最後點發布版本。

標準函式庫

新的 math/rand/v2 套件

Go 1.22 包含標準函式庫中第一個「v2」套件,math/rand/v2。與 math/rand 相較的變更詳述於 建議 #61716。最重要的變更如下

我們計畫在後續版本(可能是 Go 1.23)包含 API 移轉工具。

新的 go/version 套件

新的 go/version 套件執行了用於驗證並比較 Go 版本字串的函數。

增強的路由模式

標準函式庫中的 HTTP 路由現在更具表達力。由 net/http.ServeMux 使用的模式已經增強,能接受方法和萬用字元。

使用一個方法(例如 "POST /items/create")來註冊一個執行器,會將執行器的呼叫限制為帶有指定方法的請求。帶有方法的模式優先於不帶方法的相符模式。特別情況是,使用 "GET" 註冊一個執行器也會使用 "HEAD" 註冊它。

模式中的萬用字元(例如 /items/{id})與 URL 路徑的區段相符。透過呼叫 Request.PathValue 方法,可以存取實際的區段值。以 "…" 結尾的萬用字元(例如 /files/{path...})必須出現在模式的結尾,並與所有剩餘的區段相符。

永遠如此,以 “/” 結尾的模式與將其作為前綴的所有路徑相符。若要符合包含尾斜線的確切模式,請以 {$} 結尾,如同 /exact/match/{$}

如果兩個模式的相符請求重疊,則較為明確的模式優先。如果沒有一個較為明確,則模式衝突。此規則概括了原本的優先規則,並維持註冊模式的順序無關緊要的特性。

這項變更會以些微的方式破壞向後相容性,有些顯而易見(帶有 “{” 和 “}” 的模式行為不同),有些則較不明顯(已改善對經過逸出的路徑的處理)。此變更由名為 httpmuxgo121GODEBUG 欄位控制。設定 httpmuxgo121=1 以還原舊有行為。

函式庫的次要變更

永遠如此,函式庫會進行各種次要的變更與更新,符合 Go 1 相容性承諾。這裡未列舉的各種效能改善事項。

archive/tar

新的方法 Writer.AddFS 會將來自 fs.FS 的所有檔案加入至檔案庫中。

archive/zip

新的方法 Writer.AddFS 會將來自 fs.FS 的所有檔案加入至檔案庫中。

bufio

SplitFunc 傳回 ErrFinalToken 搭配 `nil` 令牌時,Scanner 現在會立即停止。以前會在停止前報告最後的空令牌,但通常不是期望的動作。想要報告最後的空令牌的呼叫方可以通过傳回 []byte{} 而不是 nil 來執行此操作。

cmp

新的函式 Or 會傳回序列中第一個非零值。

crypto/tls

ConnectionState.ExportKeyingMaterial 現在會回傳錯誤,除非正在使用 TLS 1.3,或伺服器和用戶端都支援 `extended_master_secret` 擴充。自 Go 1.20 起,crypto/tls 已支援這個擴充。可以使用 tlsunsafeekm=1 GODEBUG 設定來停用此功能。

預設情況下,如果沒有使用 config.MinimumVersion 指定,crypto/tls 伺服器的最低版本提供為 TLS 1.2,和 crypto/tls 用戶端的行為相符。可以使用 tls10server=1 GODEBUG 設定來還原此變更。

預設情況下,在 TLS 1.3 前的交握期間,用戶端或伺服器都不再提供不支援 ECDHE 的加密組。可以使用 tlsrsakex=1 GODEBUG 設定來還原此變更。

crypto/x509

可以使用新的 CertPool.AddCertWithConstraint 方法為根憑證增加自訂約束,套用在建立鏈的過程中。

在 Android 上,根憑證現在會從 `data/misc/keychain/certs-added` 以及 `system/etc/security/cacerts` 載入。

新的類型 OID 支援元件大於 31 位元的 ASN.1 物件識別碼。使用這個類型的新的欄位 Policies 會新增到 Certificate 結構,現在會在解析期間填入。任何無法使用 asn1.ObjectIdentifier 表示的 OID 都會出現在 Policies,但不會出現在舊的 PolicyIdentifiers 欄位。呼叫 CreateCertificate 時,會忽略 Policies 欄位,而且會從 PolicyIdentifiers 欄位取得政策。使用 x509usepolicies=1 GODEBUG 設定會反過來執行這個動作,從 Policies 欄位填入憑證政策,而且會忽略 PolicyIdentifiers 欄位。我們可能會在 Go 1.23 中變更 x509usepolicies 的預設值,讓 Policies 成為封送的預設欄位。

database/sql

新的 Null[T] 類型提供一種方法,可以對任何欄位類型掃描可為空值欄位。

debug/elf

定義常數 R_MIPS_PC32 來與 MIPS64 系統一起使用。

針對 LoongArch 系統定義了額外的 R_LARCH_* 常數。

編碼

新增至 Encoding 類型的方法 AppendEncodeAppendDecode,這些方法會加入到各個套件中的 encoding/base32encoding/base64encoding/hex 中,這些方法會透過處理位元組切片的緩衝區管理來簡化編碼和從位元組切片解碼。

如果 padding 引數是 negative 值,而非 NoPadding,方法 base32.Encoding.WithPaddingbase64.Encoding.WithPadding 現在會異常中斷。

encoding/json

編組和編碼功能現在會將 '\b''\f' 字元逸出為 \b\f,而非 \u0008\u000c

go/ast

語法識別符號解析 相關的下列宣告現在已 棄用Ident.ObjObjectScopeFile.ScopeFile.UnresolvedImporterPackageNewPackage。一般來說,識別符號無法在沒有類型資訊的情況下得到精確解析。舉例來說,考慮在 `T{K: ""}` 中的識別符號 `K`:如果 T 是對應類型,`K` 可能會是 local 變數名稱,或者如果 T 是結構類型,則有可能是欄位名稱。新的程式應該使用 go/types 套件來解析識別符號;詳細資訊請參閱 ObjectInfo.UsesInfo.Defs

新的 ast.Unparen 函式會從 expression 中移除任何附帶的 括弧

go/types

Alias 類型表示類型別名。先前,類型別名未明確表示,因此對類型別名的參照等於將別名類型明確說明出來,而且別名名稱已遺失。新的表示法保留了中介 Alias。這啟用了改善的錯誤回報(可回報類型別名名稱),而且容許更佳地處理涉及類型別名的循環類型宣告。在未來的版本中,Alias 類型也會載入 類型參數資訊。新的函式 Unalias 會傳回由 Alias 類型表示的實際類型(或任何其他 Type)。

由於 Alias 類型可能會中斷不知不覺中去檢查它們的現有類型切換,因此此功能會受到名為 gotypesaliasGODEBUG 欄位控制。在 gotypesalias=0 的情況下,所有行為都如先前一樣,而且永遠不會建立 Alias 類型。在 gotypesalias=1 的情況下,Alias 類型會被建立,而且客戶端必須預期它們。預設值為 gotypesalias=0。在未來的版本中,預設值會變更為 gotypesalias=1go/types 的客戶端強烈建議儘快調整它們的程式碼,以便搭配 gotypesalias=1 運作,以盡早消除問題。

Info 結構現在會匯出 FileVersions 對應,其會提供每個檔案的 Go 版本資訊。

新的輔助方法 PkgNameOf 會傳回給定匯入宣告的本機套件名稱。

SizesFor 的實作已經調整,以便在 SizesFor 的編譯器引數為 "gc" 時,計算與編譯器相同的類型大小。類型檢查程式所使用的預設 Sizes 實作現在為 types.SizesFor("gc", "amd64")

代表函式主體的詞法環境區塊 (Scope) 的起始位置 (Pos) 已變更:它過去從函式主體的開啟花括弧開始,但現在從函式的 func 權杖開始。

html/template

JavaScript 範本文字資料現在可能包含 Go 範本程式,而且解析包含此資料的範本將不再傳回 ErrJSTemplate。類似地,GODEBUG 設定 jstmpllitinterp 也不再有任何作用。

io

新的 SectionReader.Outer 方法會傳回傳遞給 NewSectionReaderReaderAt、偏移量和大小。

log/slog

新的 SetLogLoggerLevel 函式會控制 sloglog 套件之間橋接的等級。它會設定呼叫頂層 slog 記錄函式的最低等級,而且會設定呼叫通過 sloglog.Logger 的等級。

math/big

新的方法 Rat.FloatPrec 計算以浮點數準確表示有理數所需的十進位小數位數,並判斷一開始是否可能準確表示為十進位數字。

net

io.CopyTCPConn 複製到 UnixConn 時,它現在會使用 Linux 的 splice(2) 系統呼叫(如果可能),使用新的方法 TCPConn.WriteTo

使用「-tags=netgo」建置時使用的 Go DNS 解析器,現在會在進行 DNS 查詢之前,在位於 %SystemRoot%\System32\drivers\etc\hosts 的 Windows 主機檔案中搜尋相符的名稱。

net/http

新的函式 ServeFileFSFileServerFSNewFileTransportFS 是現有 ServeFileFileServerNewFileTransport 的版本,用於操作 fs.FS

HTTP 伺服器和用戶端現在會拒絕包含無效空 Content-Length 標頭的請求和回應。之前的行為可能會透過設定 GODEBUG 欄位 httplaxcontentlength=1 而復原。

新的方法 Request.PathValue 從請求回傳路徑萬用字元值,而新的方法 Request.SetPathValue 設定請求的路徑萬用字元值。

net/http/cgi

執行 CGI 程序時,PATH_INFO 變數現在會持續設定為空字串或以 / 字元為開頭的值,這是 RFC 3875 所要求的。之前,某些組合的 Handler.Root 和請求 URL 都有可能違反此要求。

net/netip

新的 AddrPort.Compare 方法比對兩個 AddrPort

os

在 Windows 上,Stat 函式現在會遵循所有連結到系統中其他命名實體的重新分析點。之前它只會遵循 IO_REPARSE_TAG_SYMLINKIO_REPARSE_TAG_MOUNT_POINT 重新分析點。

在 Windows 上,傳遞 O_SYNCOpenFile 現在會導致寫入作業直接進行到磁碟,這等於 Unix 平台上的 O_SYNC

在 Windows 上,ReadDirFile.ReadDirFile.ReaddirFile.Readdirnames 函數現在會以批次讀取目錄條目,以減少系統呼叫數量,將效能提升達 30%。

io.CopyFile 複製到 net.UnixConn 時,它現在會使用 Linux 的 sendfile(2) 系統呼叫(如果可能),使用新的方法 File.WriteTo

os/exec

在 Windows 中, LookPath 現在會忽略 %PATH% 中的空白項目,如果沒有找到可執行檔案副檔名來解析一個明確的名稱,則會傳回 ErrNotFound(而不是 ErrNotExist)。

在 Windows 中, CommandCmd.Start 如果執行檔的路徑已經是絕對路徑且有可執行檔案副檔名,就不會再呼叫 LookPath。另外, Cmd.Start 現在不會再將解析出的副檔名寫回 Path 欄位,所以現在可以在呼叫 String 方法時同時呼叫 Start

reflection

Value.IsZero 方法現在會對浮點數或複數的負零傳回 `true`,如果結構值有一個空白欄位(名為 _ 欄位)卻有非零值,也會傳回 `true`。這些變更使得 IsZero 與在語言中使用 == 運算子來比較一個值是否等於零一致。

PtrTo 函式已不建議使用,請改用 PointerTo

新的函式 TypeFor 會傳回代表類型參數 T 的 Type。先前要取得一個類型的 reflect.Type 值,必須使用 reflect.TypeOf((*T)(nil)).Elem()。現在可以寫成 reflect.TypeFor[T]()

runtime/metrics

四個新的直方圖量測指標 /sched/pauses/stopping/gc:seconds/sched/pauses/stopping/other:seconds/sched/pauses/total/gc:seconds/sched/pauses/total/other:seconds 提供更多關於停止世界的暫停的詳細資訊。「停止」量測指標報告從決定停止世界到所有 go 程都已停止的時間。「全部」量測指標報告從決定停止世界到世界再次啟動所花費的時間。

/gc/pauses:seconds 量測指標已不建議使用,因為它等同於新的 /sched/pauses/total/gc:seconds 量測指標。

/sync/mutex/wait/total:seconds 現在包含了執行時期內部鎖的爭用,以及 sync.Mutexsync.RWMutex

runtime/pprof

Mutex 剖析檔現在會根據被 mutex 阻擋的 go 程數量縮放爭用。這提供了更精確的表示,來說明 mutex 在 Go 程式中會有多大的瓶頸。例如,如果有 100 個 go 程被一個 mutex 阻擋了 10 毫秒,那麼現在 mutex 剖析檔會記錄 1 秒的延遲,而不是 10 毫秒的延遲。

互斥鎖剖析現在也包含執行時期內部鎖定競爭,除了 sync.Mutexsync.RWMutex。執行時期內部鎖定上的競爭總是在 runtime._LostContendedRuntimeLock 報告。未來的版本將在這些案例中加入完整的堆疊追蹤。

Darwin 平台上的 CPU 剖析現在包含該程序的記憶體對應,讓 pprof 工具能夠以反組譯檢視畫面顯示。

runtime/trace

執行追蹤器已於此次版本中徹底改造,解決了幾個長期存在的問題,並為執行追蹤開闢了新的使用案例。

執行追蹤現在大多數平台上(不包含 Windows)都使用作業系統時鐘,因此可以讓它們與較低階組件產生的追蹤相關聯。執行追蹤不再依賴平台時鐘的可靠性來產出正確的追蹤。執行追蹤現在會定期進行動態分區,因此可以用流式處理方式。執行追蹤現在包含所有系統呼叫的完整持續時間。執行追蹤現在包含作業系統執行 goroutine 的執行緒資訊。啟動和停止執行追蹤的延遲效應已大幅度降低。執行追蹤現在可以在垃圾回收標記階段開始或結束。

為了讓 Go 開發人員能善用這些改進,一個實驗性質的追蹤讀取套件可以在 golang.org/x/exp/trace 取得。請注意,這個套件目前只能使用由使用 Go 1.22 建置的程式產生的追蹤。請嘗試此套件並在 提案的相關議題 提供意見回饋。

如果你遇到新的執行追蹤器實作的任何問題, puedes 用 GOEXPERIMENT=noexectracer2 建置你的 Go 程式以切換回舊的實作。如果你這樣做,請提交一個問題,否則這個選項將會在未來的版本中移除。

切片

新的函式 Concat 會串接多個切片。

會縮減切片大小的函式 (DeleteDeleteFuncCompactCompactFuncReplace) 現在會將新長度和舊長度之間的元素歸零。

如果參數 i 超出範圍,Insert 現在永遠都會發生錯誤。如果沒有要插入的元素,它以前不會在這種情況下發生錯誤。

syscall

自 Go 1.4 起,syscall 套件便已 凍結,並於 Go 1.11 中標示為已棄用,導致許多編輯器開始對使用此套件發出警告。然而,有些未棄用的功能仍需要使用 syscall 套件,例如 os/exec.Cmd.SysProcAttr 欄位。為避免在此類程式碼上出現不必要的警告,因此 syscall 套件不再標示為已棄用。此套件仍然對新增功能凍結,而且仍建議新程式碼盡可能使用 golang.org/x/sys/unixgolang.org/x/sys/windows

於 Linux 中,新的 SysProcAttr.PidFD 欄位可以在透過 StartProcessos/exec 啟動子程序時取得 PID FD。

於 Windows 中,現將 O_SYNC 傳遞給 Open 會導致寫入作業直接前往磁碟,相當於 Unix 平台上的 O_SYNC

testing/slogtest

新的 Run 函式使用子測試來執行測試案例,提供更細微的控制權。

移植

Darwin

於 64 位元 x86 架構 (darwin/amd64 移植) 上執行 macOS 時,Go 工具鏈現在會預設產生位置無關的可執行檔 (PIE)。您可以透過指定 -buildmode=exe 建置 flag 來產生非 PIE 的二進位檔。於 64 位元 ARM 架構的 macOS (darwin/arm64 移植) 上,Go 工具鏈已預設產生 PIE。

Go 1.22 是最後一個能在 macOS 10.15 Catalina 上執行的版本。Go 1.23 將需要 macOS 11 Big Sur 或更新版本。

Arm

GOARM 環境變數現在允許您選擇使用軟體或硬體浮點。先前有效的 GOARM 值為 567。現在,這些值還可以選擇性地加上 ,softfloat,hardfloat 來選擇浮點實作。

對於版本 5,此新選項預設為 softfloat,而對於版本 67,則預設為 hardfloat

Loong64

loong64 移植現在支援使用暫存器傳遞函式參數和結果。

linux/loong64 移植現在支援不相容的位址淨化器、記憶體淨化器、新樣式的連結器重新定位以及 plugin 建置模式。

OpenBSD

Go 1.22 新增一個實驗性移植,可將 OpenBSD 安裝在 big-endian 64 位元 PowerPC (openbsd/ppc64) 上。