Go でキャプチャされたクロージャ (for ループ変数)
Go コンパイラーは、ローカルに割り当てられた for...range ループ変数を自動的にキャプチャしません。クロージャ変数。代わりに、Go はすべての for ループ (for...range ループを含む) を同様に扱い、期待される動作を保証するためにループ変数をローカル クロージャに明示的にコピーする必要があります。
ループ変数をコピーする理由
この動作は、Go による for ループの一貫した処理に起因します。 for...range ループを含むすべてのタイプの for ループでは、ループ変数のスコープはループのブロックに設定されます。ループのブロックが終了すると、ループ変数にはアクセスできなくなります。
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...範囲ループ。ただし、クロージャは元のループ変数 k をキャプチャします。これはループ全体で同じままです。その結果、コードは同じ値のペア (10, 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 中国語 Web サイトの他の関連記事を参照してください。