Go
tips

goのforループ変数の補足問題

goのforループ変数は同じ変数を共有します。
ループ内の変数の処理を後から利用しようとするとループの最後の値になってしまいます。
deferとかの時にバグをうみやすいので注意が必要です。

// loop_scope.go
package main

import (
    "fmt"
)

func main() {
    strings := []string{"a", "b", "c"}
    var printFuncList []func()

    // ループ変数sは各ループ処理で使い回されるので、無名関数からは同じ変数の値が表示されてしまいます
    for _, s := range strings {
        printFuncList = append(printFuncList, func() {
            fmt.Printf("s = \"%s\" (%p)\n", s, &s)
        })
    }

    // 何らかの処理

    for _, printFunc := range printFuncList {
        printFunc()
    }
}

// 実行結果 最後の変数の値が表示されてしまう
go run loop_scope.go
s = c (0xc00000e1e0)
s = c (0xc00000e1e0)
s = c (0xc00000e1e0)

そのため、変数宣言をする必要があります。

// loop_scope.go
package main

import (
    "fmt"
)

func main() {
    strings := []string{"a", "b", "c"}
    var printFuncList []func()

    for _, s := range strings {
        s := s // この変数宣言が必要
        printFuncList = append(printFuncList, func() {
            fmt.Printf("s = \"%s\" (%p)\n", s, &s)
        })
    }

    // 何らかの処理

    for _, printFunc := range printFuncList {
        printFunc()
    }
}

// 実行結果 適切にそれぞれの値が表示される
go run loop_scope.go
s = a (0xc00000e1e0)
s = b (0xc00000e200)
s = c (0xc00000e230)