Go開發者常犯的語法錯誤之一,便是for迴圈作用域的問題,官方預計要透過限縮for迴圈作用域的範圍,來避免這個問題所導致的程式碼錯誤。現在官方已經在Go 1.21中預覽變更,並預計要在Go 1.22中正式修正。

當開發者在編寫Go程式時,常遇到的問題發生在for迴圈,誤以為迴圈變數的作用域是每次迭代,但實際上作用域確是整個迴圈。官方用以下程式碼為例,開發者可能期望這段程式碼會輸出 "a"、"b"、"c"的某種順序排列,但實際上可能輸出的是"c"、"c"、"c"。

由於所有Goroutine都共用同一個迴圈變數,因此當迭代結束時,閉包(Closure)仍會保留對迴圈變數的參照,但該變數已經變成開發者不想要的新值。

雖然這樣的問題經常發生在並行性(Concurrency)運算時的閉包中,但實際上即便沒有Goroutine仍然會發生。官方提到,這種錯誤常在企業的生產環境中出現,例如,Let’s Encrypt的公開文件就記錄過此類錯誤。雖然目前已經有工具可以辨識這些錯誤,但是仍很難分析變數的參照是否超過迭代範圍,因此還是可能發生誤判或遺漏的狀況。

當開發者不熟悉這種作用域特性時,可能會意外地犯錯,特別是在Goroutine與閉包結合使用的時候。這並非是Go語言的錯誤,而是Go的設計決策,但由於太容易引起誤解,因此Go團隊決定進行調整。

在Go 1.22版本中,官方計畫更改for迴圈,使變數具有每次迭代的範圍,這樣的修改會讓上述案例不再存在缺陷,也能夠終結類似的問題,同時開發者也不再需要使用工具進行分析和更改。

由於需要維持現有程式碼的向後相容性,新的語義只適用於go.mod檔案宣告為Go 1.22或更高版本的模組套件中,官方提到,他們將以模組作為基礎,進而讓開發者能夠自主控制更新到新語義的節奏,同時,開發者也能夠使用//go:build指令來控制每個檔案的狀況。

舊的程式碼行為將照舊,修正只會適用更新的程式碼,Go 1.21不會嘗試編譯宣告為Go 1.22或更高版本的程式碼,由於Go 1.20.8和Go 1.19.13兩個版本也存在同樣for迴圈問題,因此用新語義編寫的程式碼,也同樣不會使用這些舊版本進行編譯,除非開發者使用非常舊且不受支援的Go版本。

熱門新聞

Advertisement