5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2018-10-08

:warning: golang 1.22からこの問題は発生しなくなっています 参考: https://go.dev/blog/go1.22

golang 1.22未満の 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()
	}
}

実行すると、最後の変数の値の"c"が表示されてしまいます。

// 実行結果(1.21.7 で確認)
go run loop_scope.go
s = "c" (0x14000010070)
s = "c" (0x14000010070)
s = "c" (0x14000010070)

// 実行結果(1.22.0 で確認)
go run loop_scope.go
s = "a" (0x14000090040)
s = "b" (0x14000090060)
s = "c" (0x14000090090)

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

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

今度は、"a", "b", "c"とそれぞれの変数の値の"c"が表示される用になります。

// 実行結果
go run loop_scope.go
s = "a" (0x14000102020)
s = "b" (0x14000102040)
s = "c" (0x14000102070)
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?