ホームページ >バックエンド開発 >Golang >Go ではなぜクロージャ内のループ変数の明示的なコピーが必要なのでしょうか?

Go ではなぜクロージャ内のループ変数の明示的なコピーが必要なのでしょうか?

Barbara Streisand
Barbara Streisandオリジナル
2024-12-24 01:44:11355ブラウズ

Why Does Go Require Explicit Copying of Loop Variables in Closures?

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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。