Go 1.19 發行說明

Go 1.19 簡介

Go 最新版本 1.19 在 Go 1.18 推出五個月後發布。大部分變更皆在工具鏈、執行時間和函式庫實作中。發行版本一如往常地保持 Go 1 相容性的承諾。我們預計大多數的 Go 程式屆時仍可以編譯並執行,如同先前版本。

語言變更

語言僅有一個微小的變更,即 針對方法宣告中類型參數的範圍做了一個 非常小的更正。現有程式不受影響。

記憶體模型

Go 記憶體模型修訂,以將 Go 與 C、C++、Java、JavaScript、Rust 和 Swift 使用的記憶體模型對齊。Go 僅提供循序一致的原子操作,而非其他語言中出現的較寬鬆形式。除了記憶體模型更新之外,Go 1.19 也在 sync/atomic 封裝中引入了 新的類型 使得使用原子值更加容易,例如 atomic.Int64atomic.Pointer[T]

LoongArch 64 位元

Go 1.19 在 Linux(GOOS=linuxGOARCH=loong64)上增加了對 Loongson 64 位元架構 LoongArch 的支援。實作的 ABI 是 LP64D。支援的最小核心版本是 5.19。

請注意,大多數現有的 LoongArch 商用 Linux 發行版都隨附較舊的內核,具有歷史不相容的系統呼叫 ABI。即使是靜態連結,編譯的二進位檔也無法在這些系統上執行。此類不受支援系統上的使用者只能使用發行版提供的 Go 套件。

RISC-V

現在,riscv64 埠支援使用暫存器傳遞函式引數和結果。基準測試顯示 riscv64 上通常效能會提升 10% 以上。

工具

文件註解

Go 1.19 增加對文件註解中的連結、清單和標題的支援。作為此變更的一部分,gofmt 現在會重新格式化文件註解,讓其呈現的意義更清楚。請參閱「Go 文件註解」以取得語法詳細資料和會由 gofmt 醒目的常見錯誤說明。作為此變更的另一部分,新的套件 go/doc/comment 提供文件註解的剖析和重新格式化功能,以及支援將其呈現為 HTML、Markdown 和文字。

新的 unix 建立約束

現在在 //go:build 行中識別建立約束 unix。如果目標作業系統(也稱為 GOOS)是 Unix 或 類 Unix 系統,便會滿足此約束。對於 1.19 版本,如果 GOOS 為以下其中一個:aixandroiddarwindragonflyfreebsdhurdillumosioslinuxnetbsdopenbsdsolaris,即表示已滿足。在未來的版本中,unix 約束可能會符合其他新支援的作業系統。

Go 指令

-trimpath 旗標(如果已設定)現在會包含在 go build 蓋入 Go 二進位檔的建立設定中,而且可以使用 go version -mdebug.ReadBuildInfo 進行檢查。

go generate 現在會在產生器的環境中明確地設定 GOROOT 環境變數,如此一來,即使使用 -trimpath 建立,產生器仍可找到正確的 GOROOT

go testgo generate 現在會將 GOROOT/bin 放置於用於子程序的 PATH 開頭,因此執行 go 指令的測試和產生器會將其解析為相同的 GOROOT

go env 現在會在 CGO_CFLAGSCGO_CPPFLAGSCGO_CXXFLAGSCGO_FFLAGSCGO_LDFLAGSGOGCCFLAGS 變數中引述包含空白的項目。

go list -json 現可接受要填入的 JSON 欄位之逗號分隔清單。如果指定清單,JSON 輸出將僅包含那些欄位,且 go list 可能會避免運算未包含欄位的作業。在某些情況下,這可能會壓抑原本會報告的錯誤。

go 指令現快取載入部分模組所需的資訊,這應會加快部分 go list 呼叫的速度。

Vet

vet 檢查器「errorsas」現會在 errors.As 呼叫時第二個引數類型為 *error(常見錯誤)時回報。

執行時間

執行時間現包含對軟性記憶體限制的支持。此記憶體限制包含 Go 堆積與執行時間管理的其他所有記憶體,並排除外部記憶體來源,例如二進位檔本身的對應、用其他語言管理的記憶體,以及作業系統會保留而 Go 程式可使用的記憶體。此限制可透過 runtime/debug.SetMemoryLimit 或相當的 GOMEMLIMIT 環境變數管理。此限制會與 runtime/debug.SetGCPercent / GOGC 共同作業,即使 GOGC=off,也會遵守,這讓 Go 程式持續最大化使用記憶體限制,進而改善某些狀況的資源效率。請查看 GC 指南,瞭解更詳細地說明軟性記憶體限制的詳細指南,還有各種一般用例和情境。請注意,約數十或數兆位元組大小的小記憶體限制不太可能受到尊重,原因在於外部延遲因素,例如作業系統排程。請查看 問題 52433,進一步瞭解詳細資訊。數百或數兆位元組大小的較大型記憶體限制是穩定且適合生產使用的。

為限制當程式動態堆積大小接近軟性記憶體限制時 GC 衝擊的影響,Go 執行時間也試著將 GC 總 CPU 使用率限制在 50%(不含閒置時間),並選擇使用更多記憶體,而非防止應用程式進度。在實務上,我們預期此限制僅會在特殊案例中發揮作用,而新的 執行時間指標 /gc/limiter/last-enabled:gc-cycle 會在最後一次發生時回報。

當應用程式閒置到足以強制週期性 GC 迴圈時,執行時間現會在閒置作業系統執行緒中排程更少的 GC 工作常式。

執行時間現在會根據常式中動態堆積的歷史平均使用率,來配置初始的動態堆積。這可以避免在一般狀況下所需的早期的堆積成長和複製,但可能會浪費最多 2 倍的空間在低於平均值的動態堆積上。

在 Unix 作業系統上,匯入套件 os 的 Go 程式現在會自動將目前開啟檔案限制 (RLIMIT_NOFILE) 增加至允許的最大值;也就是說,它們會將軟性限制變更為符合硬性限制。這修正了某些系統中為了相容使用 select 系統呼叫的非常舊版 C 程式而設定的人為低限制。Go 程式無法透過此限制得益,因此即使像 gofmt 這種簡單的程式在並行程式處理大量檔案時也常常會用盡檔案描述詞。此變更的其中一個影響為,會依序執行非常舊版 C 程式的子程式的 Go 程式可能會使用過高的限制來執行這些程式。這可以在呼叫 Go 程式之前設定硬性限制來修正。

無法復原的致命錯誤 (例如並發的映射寫入,或解鎖未鎖定的互斥鎖) 現在會印出較簡單的追蹤,不包含執行時間元資料 (等同於致命性恐慌),除非 GOTRACEBACK=systemcrash。執行時間內部致命錯誤追蹤會永遠包含完整元資料,與 GOTRACEBACK 的值無關。

已在 ARM64 上加入對偵錯器注入函式呼叫的支援,使用已更新以使用此功能的偵錯器時,使用能從其二進位檔中呼叫函式的使用者可以參與互動偵錯階段。

在 Go 1.18 中加入的 位址偵測防腐敗 現在可以更精準處理函式引數與全域變數。

編譯器

編譯器現在使用 跳躍表 實作大型整數與字串的 switch 陳述式。switch 陳述式的效能改善幅度不一,但可能快上大約 20%。 (僅限於 GOARCH=amd64GOARCH=arm64)

Go 編譯器現在需要 -p=importpath 旗標才能編譯可連結的物件檔。此旗標已由 go 指令和 Bazel 提供。任何直接呼叫 Go 編譯器的其他建置系統都必須確認它們也傳遞了此旗標。

Go 編譯器不再接受 -importmap 旗標。直接呼叫 Go 編譯器的建置系統必須改用 -importcfg 旗標。

組譯器

與編譯器類似,組譯器現在需要 -p=importpath 旗標才能編譯可連結的物件檔。此旗標已由 go 指令提供。任何直接呼叫 Go 組譯器的其他建置系統都必須確認它們也傳遞了此旗標。

連結器

在 ELF 平台上,連結器現在會以標準 gABI 格式 (SHF_COMPRESSED) 發布壓縮的 DWARF 區段,而不是過往的 .zdebug 格式。

標準函式庫

新的原子類型

套件 sync/atomic 定義新的原子型別 BoolInt32Int64Uint32Uint64UintptrPointer。這些型別隱藏其底層值,以強制所有存取操作使用原子 API。在呼叫端,Pointer 也避免轉換至 unsafe.Pointer。即使在 32 位元系統中,Int64Uint64 仍然會在結構和配置資料中自動調整至 64 位元邊界。

PATH 搜尋

CommandLookPath 不再容許根據目前的目錄範圍找出 PATH 搜尋的結果。此舉除了移除 常見的安全問題來源,但也可能會導致部分現有機制出錯,例如依賴於使用 exec.Command("prog") 執行目前的目錄中的二進位檔案 prog (或者在 Windows 中執行 prog.exe) 的機制。如需瞭解最佳化這些機制的相關資訊,請參閱 os/exec 套件的文件。

在 Windows 中,CommandLookPath 現在會遵循 NoDefaultCurrentDirectoryInExePath 環境變數,此舉讓 Windows 系統得以在 PATH 搜尋中停用隱含搜尋方式的「.」預設搜尋。

函式庫的次要變更

如同以往,我們對函式庫進行了無數次變更和更新,並將 Go 1 相容性承諾 列為考量重點。此外,我們也進行了許多效能改善,但未逐一列出。

archive/zip

Reader 現在會略過 ZIP 檔案開頭的非 ZIP 資料,這項更新和多數其他實作相符。舉例而言,此更新有助於讀取 Java JAR 檔案。

crypto/elliptic

我們始終將使用無效的曲線點視為未定義的行為(這些點為 IsOnCurve 方法傳回 false 的點,且永不會由 UnmarshalCurve 方法(針對有效點)傳回),這類行為可能會導致金鑰復原攻擊。如果將無效點提供給 MarshalMarshalCompressedAddDoubleScalarMult,這些方法現在會引發恐慌。

現在,P224P384P521 曲線上的 ScalarBaseMult 營運速度最多快達三倍,也因此,在一些 ECDSA 營運中得到類似的速度提升。已經將一般的 (非平台最佳化) P256 實作替換為從形式化驗證模型衍生而來的實作;這可能會導致 32 位元平台的速度大幅降低。

crypto/rand

Read 不會再緩衝作業系統之間呼叫時,取得的隨機資料。如果應用程式以高頻率執行許多小讀取,可以考慮包裝 Readerbufio.Reader,以求效能上的原因,小心使用 io.ReadFull,確保沒有發生部分讀取的情況。

在 9 號計畫中,已經重新實作 Read,移除 ANSI X9.31 演算法,並改以快速金鑰消除產生器取代。

Prime 實作已變更,只使用拒絕抽樣。在非密碼學情境中產生小質數時,這會移除偏差,移除可能的微小計時洩漏,而且可以讓行為更符合 BoringSSL,同時精簡實作。和前一個實作相比,此變更針對給定的隨機來源串流產生不同的輸出,這可能會中斷預期特定確定性隨機來源可以產生特定結果而撰寫的測試。為了幫助預防未來發生此類問題,現在實作在輸入串流方面蓄意非確定性。

crypto/tls

GODEBUG 選項 tls10default=1 已移除。仍然可以透過設定 Config.MinVersion,來啟用用戶端的 TLS 1.0。

現在,TLS 伺服器和用戶端會拒絕 TLS 握手中的重複擴充元件,如同 RFC 5246 第 7.4.1.4 節和 RFC 8446 第 4.2 節的要求。

crypto/x509

CreateCertificate 不再支援建立 SignatureAlgorithm 設為 MD5WithRSA 的憑證。

CreateCertificate 不再接受負序號。

如果產生的憑證沒有擴充元件,CreateCertificate 將不再產生空的 SEQUENCE。

原定在 Go 1.19 中移除的 GODEBUG 選項 x509sha1=1,已延後到未來的版本。使用此選項的應用程式應該開始移轉。自 2017 年以來,已經證實有針對 SHA-1 的實際攻擊,而且公正信任的憑證授權自 2015 年以來,就沒有發出過 SHA-1 憑證了。

ParseCertificateParseCertificateRequest 現在會拒絕含有重複擴充元件的憑證和 CSR。

新的 CertPool.CloneCertPool.Equal 方法分別允許複製 CertPool 和檢查兩個 CertPool 的等效性。

新的函數 ParseRevocationList 提供了一個更快速、更安全的 CRL 剖析器,這會傳回一個 RevocationList。剖析 CRL 時也會填入新的 RevocationList 欄位 RawIssuerSignatureAuthorityKeyIdExtensions,這些欄位會被 CreateRevocationList 忽略。

新的方法 RevocationList.CheckSignatureFrom 檢查 CRL 上的簽章是否是一個有效簽章,來自一個 Certificate

為了 ParseRevocationListParseCRLParseDERCRL 函數現已不再使用。為了 RevocationList.CheckSignatureFromCertificate.CheckCRLSignature 方法現已不再使用。

改善了 Certificate.Verify 的路徑建構器,因此現在應該會產生更佳的鏈條和/或在複雜的場景中更有效率。現在也對非葉子憑證執行名稱限制。

crypto/x509/pkix

類型 CertificateListTBSCertificateList 已不再使用。應改用新的 crypto/x509 CRL 功能

debug/elf

新的常數 EM_LOONGARCHR_LARCH_* 支援 loong64 埠。

debug/pe

新的 File.COFFSymbolReadSectionDefAux 方法,它會傳回一個 COFFSymbolAuxFormat5,提供存取 PE 檔案區段中的 COMDAT 資訊。它們受新的 IMAGE_COMDAT_*IMAGE_SCN_* 常數支援。

encoding/binary

新的介面 AppendByteOrder 提供用於加附 uint16uint32uint64 到一個位元組切片中,有效率的方法。 BigEndianLittleEndian 現在會實作這個介面。

類似地,新的函數 AppendUvarintAppendVarintPutUvarintPutVarint 的加附版本。

encoding/csv

新的方法 Reader.InputOffset 以位元組位移量來報告讀取器的目前輸入位置,類似於 encoding/jsonDecoder.InputOffset

encoding/xml

新的方法 Decoder.InputPos 會回報讀取器的目前輸入位置行和行,類比於 encoding/csvDecoder.FieldPos

flag

新的函式 TextVar 定義一個 flag,其值會實作 encoding.TextUnmarshaler,允許命令列 flag 變數具有諸如 big.Intnetip.Addrtime.Time 等類型。

fmt

新的函式 AppendAppendfAppendln 會將格式化資料附加到位元組分片。

go/parser

解析器現在會將 ~x 識別為具備運算子 token.TILDE 的一元運算式,當在不正確的語境中使用型別限制,例如 ~int,這樣會允許更好的錯誤復原。

go/types

新的方法 Func.OriginVar.Origin 會回傳在型別建立期間產生的合成 FuncVar 物件之對應 Object

現在不再可能透過對 Named.UnderlyingNamed.Method 的遞迴呼叫來產生無窮個相異但相同的 Named 型別建立。

hash/maphash

新的函式 BytesString 提供一個有效率的方式來對單一位元組分片或字串進行雜湊。它們相當於使用具有單一寫入的一般 Hash,但它們會避免小型輸入的設定開銷。

html/template

型別 FuncMap 現在是別名,指向 text/templateFuncMap,而不是它自己的命名型別。這樣允許編寫針對設定中任一 FuncMap 來操作的程式碼。

Go 1.19.8 及之後版本 禁止在 ECMAScript 6 範本字串中進行動作。 設定 GODEBUG=jstmpllitinterp=1 可以還原這項行為。

image/draw

當目的地和來源影像皆為 image.NRGBAimage.NRGBA64 時,具有 Src 運算子的 Draw 會保留未預先乘以 alpha 的色彩。這將還原 Go 1.18 程式庫最佳化不慎引進的行為變更;程式碼現在會比照 Go 1.17 及更早版本中的行為。

io

NopCloser 的結果現在只要其輸入有實作,就會實作 WriterTo

MultiReader 的結果現在無條件地實現 WriterTo。如果任何底層閱讀器未實作 WriterTo,則適當地模擬它。

mime

僅針對 Windows,mime 套件現在會忽略註冊表項目記錄,extension .js 應具備 MIME 類型 text/plain。這是 Windows 系統中常見的意外錯誤設定。其結果是 .js 會具備預設 MIME 類型 text/javascript; charset=utf-8。現在,預期 Windows 上有 text/plain 的應用程式必須明確呼叫 AddExtensionType

mime/multipart

在 Go 1.19.8 及之後的版本中,此套件設定要處理的 MIME 資料大小限制,以防範惡意輸入。Reader.NextPartReader.NextRawPart 將部分中的標頭數量限制為 10000,而 Reader.ReadForm 將所有 FileHeaders 中標頭的總數量限制為 10000。可以使用 GODEBUG=multipartmaxheaders 設定調整這些限制。Reader.ReadForm 進一步將表單中的部分數量限制為 1000。可以使用 GODEBUG=multipartmaxparts 設定調整此限制。

net

純 Go 解析器現在將使用 EDNS(0) 來納入建議的最大回覆封包長度,允許回覆封包包含多達 1232 位元組 (先前的最大值為 512)。萬一此舉對本機 DNS 解析器造成問題,設定環境變數 GODEBUG=netdns=cgo 以使用基於 cgo 的解析器應可解決問題。請在 問題追蹤器 中回報任何此類問題。

當網路套件函式或方法傳回「I/O 超時」錯誤時,錯誤現在將符合 errors.Is(err, context.DeadlineExceeded)。當網路套件函式傳回「作業已取消」錯誤時,錯誤現在將符合 errors.Is(err, context.Canceled)。這些變更旨在簡化程式碼測試,以了解於哪些情況下,背景取消或逾時會導致網路套件函式或方法傳回錯誤,同時維持錯誤訊息的後向相容性。

Resolver.PreferGo 現在可在 Windows 和 Plan 9 中實作。它以前僅能在 Unix 平臺中運作。結合 Dialer.ResolverResolver.Dial,現在可以使用可攜式程式並在撥號時控管所有 DNS 名稱查詢。

net 套件現在對 Windows 上的 netgo 組建標籤提供初始支援。套件在使用時採用 Go DNS 處理常式 (與 Resolver.PreferGo 相同),而非向 Windows 索取 DNS 結果。不過,它從 Windows 偵測到的上游 DNS 伺服器在複雜的系統網路組態中可能不正確。

net/http

ResponseWriter.WriteHeader 現在支援傳送使用者自訂的 1xx 資訊性標頭。

MaxBytesReader 回傳的 io.ReadCloser 現在會在讀取極限被超過時回傳已定義的錯誤類型 MaxBytesError

HTTP 處理常式會處理沒有 Location 標頭的 3xx 回應,並將它傳回呼叫者,而非將它視為錯誤。

net/url

新的 JoinPath 函數和 URL.JoinPath 方法結合許多路徑元素,建立新的 URL

URL 類型現在區分沒有權限的 URL 和具有空權限的 URL。例如,http:///path 具有空的權限 (主機),而 http:/path 沒有權限。

URL 具有空的權限時,新的 URL 欄位 OmitHost 會設定為 true

os/exec

Cmd 如果有非空的 Dir 欄位和 nilEnv,現在會隱式設定子程序的 PWD 環境變數,使其與 Dir 相符。

新的方法 Cmd.Environ 會回報用於執行指令的環境,其中包含隱式設定的 PWD 變數。

reflect

Value.Bytes 方法現在除了片段以外,還接受可位址陣列。

方法 Value.LenValue.Cap 現在成功運作於陣列指標上,並回傳陣列的長度,與 內建的 lencap 函數相同。

regexp/syntax

Go 1.18 發行候選版本 1、Go 1.17.8 和 Go 1.16.15 包含了正規表示式剖析器的安全性修正,使其拒絕過於深層巢狀的表示式。由於 Go 修正版本沒有推出新的 API,因此解析器會在這種情況下回傳 syntax.ErrInternalError。Go 1.19 新增更具體的錯誤,syntax.ErrNestingDepth,現在解析器會回傳這個錯誤。

runtime

當二進位檔使用已設定的 -trimpath 旗標建置,而處理程序環境中沒有設定 GOROOT 變數時,現在 GOROOT 函數會回傳空的字串 (而非 "go")。

runtime/metrics

新的/sched/gomaxprocs:threads 指標報告當前的 runtime.GOMAXPROCS 值。

新的 /cgo/go-to-c-calls:calls 指標報告從 Go 到 C 的呼叫總數。此指標與 runtime.NumCgoCall 函數相同。

新的 /gc/limiter/last-enabled:gc-cycle 指標報告上次啟用 GC CPU 限制器時的 GC 週期。有關 GC CPU 限制器的詳細資訊,請參閱 執行期間注意事項

runtime/pprof

收集 goroutine 剖析時,暫停世界的暫停時間已大幅減少,並減少對應用程式的整體延遲影響。

現在在所有 Unix 作業系統中,堆積剖析中會報告 MaxRSS(先前僅在 GOOS=androiddarwinioslinux 中報告)。

runtime/race

此競爭狀態偵測器已升級為在所有受支援的平台(包括 windows/amd64openbsd/amd64,但仍維持 v2)中使用版本 v3 的執行緒消毒程式。與 v2 相較之下,它通常快 1.5 到 2 倍,且僅使用一半的記憶體並支援無限數量的 goroutine。在 Linux 上,此競爭狀態偵測器現在至少需要 glibc 版本 2.17 和 GNU binutils 2.26。

現在在 GOARCH=s390x 上支援此競爭狀態偵測器。

執行緒消毒程式上游已移除 openbsd/amd64 的競爭狀態偵測器支援,因此不太可能從 v2 更新。

runtime/trace

同時啟用追蹤和 CPU 剖析器 時,執行追蹤會包含 CPU 剖析範例作為即時事件。

sort

已重寫排序演算法以使用 模式失敗快排,這對於多個常見的場景來說較快。

有了 Find 新函數,就像 Search,但通常更容易使用:它會傳回一個額外的布林,報告是否找到相等的值。

strconv

Quote 和相關函數現在會將 U+007F 符號引為 \x7f,而非 \u007f,以符合其他 ASCII 值。

syscall

在 PowerPC(GOARCH=ppc64ppc64le)上,SyscallSyscall6RawSyscallRawSyscall6 現在會永遠傳回 0 給傳回值 r2,而非未定義的值。

在 AIX 和 Solaris 上,Getrusage 現已定義。

time

新的方法 Duration.Abs 提供一種方便又安全的取得持續時間的絕對值的方法,並將 −2⁶³ 轉換為 2⁶³−1。(這個邊界案例可能會在從零時間減去較近時間時發生。)

新的方法 Time.ZoneBounds 會傳回特定時間所處時區的開始與結束時間。您可以在迴圈中使用它來列舉特定位置的所有已知時區過渡時間。