ホームページ >バックエンド開発 >Golang >Go はクロージャでキャプチャされたループ変数をどのように処理しますか? それがなぜ重要ですか?

Go はクロージャでキャプチャされたループ変数をどのように処理しますか? それがなぜ重要ですか?

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-12-15 11:56:10805ブラウズ

How Does Go Handle Captured Loop Variables in Closures, and Why Does it Matter?

Go でキャプチャされたクロージャ (for ループ変数)

ループ変数のクロージャを理解する

Go では、コンパイラは内部で使用するループ変数を自動的にキャプチャできます。ただし、この動作はループのタイプによって異なります。 for...range ループでは、ループ変数は外側のループの反復変数への参照としてキャプチャされます。

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)
    }
}

Output

In lab1 では、キャプチャされたクロージャは、期待される個々の値ではなく、ループからの最終値を参照します。 lab2 では、作成されたクロージャが外部スコープの他の場所で参照されているのと同じループ変数を使用するため、キャプチャは引き続き最終値を参照します。 lab3 では、クロージャーはループ変数のコピーをキャプチャするため、個々の値を正確に表します。

以上がGo はクロージャでキャプチャされたループ変数をどのように処理しますか? それがなぜ重要ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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