Posted at

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)