背景
前兩天 Golang
的官方部落格更新了一篇文章:Fixing For Loops in Go 1.22
看這個標題的就是修復了 Go 迴圈的 bug,這真的是史詩級的更新;我身邊接觸到的大部分 Go 開發者都犯過這樣的錯誤,包括我自己,所以前兩年我也寫過類似的部落格:
簡單的 for 迴圈也會踩的坑
先來簡單回顧下使用使用 for 迴圈會碰到的問題:
list := []*Demo{{"a"}, {"b"}}
for _, v := range list {
go func() {
fmt.Println("name="+v.Name)
}()
}
type Demo struct {
Name string
}
預期的結果應該是列印 a,b
,但實際列印的卻是b,b
。
Let's Encrypt: CAA Rechecking bug
類似的問題連 mozilla
團隊也沒能倖免,所以也確實是一個非常常見的問題,這樣的寫法符合大部分的開發者的直覺,畢竟其他語言這麼使用也沒有問題。
當然在現階段要解決也很簡單,要麼就是在使用之前先複製一次,或者使用閉包傳參:
// 複製
list := []*Demo{{"a"}, {"b"}}
for _, v := range list {
temp:=v
go func() {
fmt.Println("name="+temp.Name)
}()
}
// 閉包
list := []*Demo{{"a"}, {"b"}}
for _, v := range list {
go func(temp *Demo) {
fmt.Println("name="+temp.Name)
}(v)
}
還好官方也意識到了這個問題:
所以在 1.22 中我們可以不用再寫這個
v:=v
這個多餘的複製語句了,也不會出現上面的問題。
我們在 1.21 中可以使用環境變數預覽這個特性:
❯ GOEXPERIMENT=loopvar go test
name=b
name=a
在 1.22 釋出後建議大家都可以升級了,將這種噁心的 bug 扼殺在搖籃裡。
1.22 後帶來了一個好訊息是今後少了一道面試題,壞訊息是又新增了一個 1.22 版本帶來了哪些變化的面試題?
更多詳情可以參看官方播客:https://go.dev/blog/loopvar-preview