在Go 中,編譯器可以自動捕捉循環變數以在內部使用閉包,但這種行為會根據循環類型而變化。在 for...range 循環中,循環變數被捕獲為對外循環迭代變數的引用。
Go 對待 for.. .range 迴圈和其他 for 迴圈類似。因此,for...range 循環中循環變數的捕獲閉包引用與外循環變數相同的記憶體位置。
在這種情況下,任何修改對捕獲的閉包變量所做的操作也會影響外循環的變量,可能會導致意外的行為。為了避免這個問題,有必要在閉包中建立循環變數的副本,如下面的「Value...range」範例所示。
提供的程式碼片段說明了擷取循環變數的引用與擷取其值之間的區別:
func main() { lab1() // captured closure is not what is expected lab2() // captured closure is not what is expected lab3() // captured closure behaves ok } func lab3() { m := make(map[int32]int32) for i := 1; i <= 10; i++ { m[i] = i } l := [](func() (int32, int32)){} for k, v := range m { kLocal, vLocal := k, v // (C) captures just the right values assigned to k and v l = append(l, func() (int32, int32) { return kLocal, vLocal }) } for _, x := range l { k, v := x() fmt.Println(k, v) } } func lab2() { m := make(map[int32]int32) for i := 1; i <= 10; i++ { m[i] = i } l := [](func() (int32, int32)){} for k, v := range m { l = append(l, func() (int32, int32) { kLocal, vLocal := k, v // (B) captures just the last values assigned to k and v from the range return kLocal, vLocal }) } for _, x := range l { k, v := x() fmt.Println(k, v) } } func lab1() { m := make(map[int32]int32) for i := 1; i <= 10; i++ { m[i] = i } l := [](func() (int32, int32)){} for k, v := range m { l = append(l, func() (int32, int32) { return k, v }) // (A) captures just the last values assigned to k and v from the range } for _, x := range l { k, v := x() fmt.Println(k, v) } }
在lab1 中,捕獲的閉包引用循環中的最終值而不是預期的單一值。在 lab2 中,擷取仍然引用最終值,因為建立的閉包使用與外部作用域中其他地方引用的相同循環變數。在 lab3 中,閉包會擷取循環變數的副本,因此它們準確地表示各個值。
以上是Go 如何處理閉包中捕獲的循環變量,以及為什麼它很重要?的詳細內容。更多資訊請關注PHP中文網其他相關文章!