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) } }
In lab1 では、キャプチャされたクロージャは、期待される個々の値ではなく、ループからの最終値を参照します。 lab2 では、作成されたクロージャが外部スコープの他の場所で参照されているのと同じループ変数を使用するため、キャプチャは引き続き最終値を参照します。 lab3 では、クロージャーはループ変数のコピーをキャプチャするため、個々の値を正確に表します。
以上がGo はクロージャでキャプチャされたループ変数をどのように処理しますか? それがなぜ重要ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。