首頁 >後端開發 >Golang >Go語言中的for迴圈有多坑?

Go語言中的for迴圈有多坑?

藏色散人
藏色散人轉載
2022-11-07 16:48:132612瀏覽

本文由golang教學專欄跟大家介紹關於Go for 迴圈的面試問題,不知道大家對for迴圈了解多少,有沒有覺得很坑?以下就給大家詳細聊聊for相關問題,希望對需要的朋友有幫助!

不知道有多少 Go 的面試題和洩露,都和 for 迴圈有關。今天我在周末認真一看,發現了 redefining for loop variable semantics 。

著名的硬核大佬 Russ Cox 表示他一直在研究這個問題,並表示十年的經驗表明了當前語義的代價是很大的。

問題

  • 案例一:取地址符

在Go 語言中,我們寫for 語句時有時會出現運行和猜想的結果不一致。例如以下第一個案例的程式碼:

var all []*Itemfor _, item := range items {
	all = append(all, &item)
}

這段程式碼有問題嗎?變數 all 內的 item 變量,儲存進去的是什麼?是每次循環的 item 值嗎?

其實在 for 迴圈時,每次存入變數 all 的都是相同的 item,也就是最後一個迴圈的 item 值。

這是 Go 面試裡常出現的題目,結合 goroutine 更風騷,畢竟還會有亂序輸出等問題。

如果你想解決這個問題,就需要把程式改寫成如下:

var all []*Itemfor _, item := range items {
	item := item
	all = append(all, &item)
}

要重新宣告一個 item 變數把 for 迴圈的 item 變數給儲存下來再追加進去。

  • 案例二:閉包函數

接下來是第二個案例的程式碼:

var prints []func()for _, v := range []int{1, 2, 3} {
	prints = append(prints, func() { fmt.Println(v) })
}for _, print := range prints {	print()
}

這段程序的輸出結果是什麼?沒有 & 取位址符,是輸出 1,2,3 嗎?

輸出結果是 3,3,3。這又是為什麼?

問題的重點之一,關注到閉包函數,實際上所有閉包都列印的是相同的 v。輸出 3,是因為在 for 迴圈結束後,最後 v 的值被設定為了 3,僅此而已。

如果想要達到預期的效果,依然是使用萬能的再賦值。改寫後的程式碼如下:

for _, v := range []int{1, 2, 3} {
		v := v
		prints = append(prints, func() { fmt.Println(v) })
	}

增加 v := v 語句,程式輸出結果為 1,2,3。 仔細翻翻你寫過的 Go 工程,是不是都很熟悉?就這改造方法,贏了。

尤其是配合上 Goroutine 的寫法,很多同學會比較容易在此翻車。

解決方案

  • 修復想法

實際上Go 核心團隊在內部和社群已經討論過許久,希望重新定義for 迴圈的語法。要達到的目的是:使循環變數每次迭代而不是每次循環

解決的方法是:在每個迭代變數x 的每個循環體開頭,加上一個隱式的再賦值,也就是x := x,就能夠解決上述程序中所隱含的坑。和我們現在做的一樣,只不過我們是自己手動加的,Go 團隊做的是希望在編譯器內隱式處理。

  • 讓使用者自己決定

比較尷尬的是Go 團隊在Proposal: Go 2 transition 中禁止重新定義語言,所以rsc 不能直接這麼乾。

因此將會由使用者自己決定控制這個 “破壞”,方式將會是根據每個套件的 go.mod 檔案中的 go 行更改語義。

如果我們是在 Go1.30 對本文討論的 for 迴圈改為迭代,那麼在 go.mod 檔案中的 go 版本宣告就是一個關鍵。

如下圖示:

Go 1.30 或更高版本將會每次迭代變量,而早期 Go 版本的將每次循環變數。

如此一來上述提到的 for 迴圈問題都會在一定範圍內解決。

總結

for 迴圈時的變數問題,一直是各大Go 考官愛考的題目,另外也確實在實際程式設計Go 程式碼時會遇到這類坑。

雖然 rsc 希望在 go.mod 檔案上開創先河,利用 go 版本的聲明,去修改語意(不允許新增和刪除)。這無疑是給 Go1 相容性保障開了一個後門。

如果實施,本次變更會導致 Go 的前後版本語意有所不同。還不如變成一個 go.mod 檔案的一個語義開關。

這顯然是一個很折騰的思考題。

以上是Go語言中的for迴圈有多坑?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除