Go 中捕獲的閉包(for 循環變數)
Go 編譯器不會自動捕獲for...range 循環變數作為本地分配閉包變數。相反,Go 類似地對待所有 for 迴圈(包括 for...range 迴圈),需要將迴圈變數明確複製到局部閉包中以確保預期的行為。
複製迴圈變數的原因
此行為源自於 Go 對 for 迴圈的一致處理。在所有類型的 for 循環中,包括 for...range 循環,循環變數的作用域為循環區塊。循環塊結束後,循環變數將不再可存取。
在 for...range 迴圈中,循環變數在迴圈的每次迭代中都會被初始化為新值。但是,在循環內建立的閉包繼續引用原始循環變數。為了確保閉包具有循環變數的獨立副本,必須將其指派給閉包內的局部變數。
程式碼範例
以下是一個範例這說明了複製循環變數的必要性:
func main() { m := make(map[int32]int32) for i := int32(1); i <= 10; i++ { m[i] = i } l := make([]func() (int32, int32), 0) for k, v := range m { l = append(l, func() (int32, int32) { return k, v }) } for _, f := range l { k, v := f() fmt.Println(k, v) } }
在此範例中,循環變數k 預計在for...range 的每次迭代期間發生變化環形。然而,閉包捕獲了原始循環變數 k,該變數在整個循環中保持不變。結果,代碼列印同一對值 (10, 10) 十次。
解:複製循環變數
要解決此問題,循環變數必須複製到閉包內的局部變數:
func main() { m := make(map[int32]int32) for i := int32(1); i <= 10; i++ { m[i] = i } l := make([]func() (int32, int32), 0) for k, v := range m { kLocal, vLocal := k, v l = append(l, func() (int32, int32) { return kLocal, vLocal }) } for _, f := range l { k, v := f() fmt.Println(k, v) } }
現在,閉包會擷取局部變數kLocal 和vLocal,它獨立地保存在for...range 循環的每次迭代期間分配的值。代碼正確列印了預期的值對 (1, 1), (2, 2), ..., (10, 10)。
以上是為什麼 Go 需要在閉包中明確複製循環變數?的詳細內容。更多資訊請關注PHP中文網其他相關文章!