結論
range の左辺はただの変数である。
繰り返しのたびに再代入しているだけなので、気をつける。
現象
上手い例がかけないが、例えば以下の状況
type Foo struct {
Item string
}
foo1 := Foo{"1"}
foo2 := Foo{"2"}
foo3 := Foo{"3"}
slice := []Foo{foo1, foo2, foo3}
var foos []*Foo
for _, foo := range slice {
foos = append(foos, &foo)
}
for _, foo := range foos {
fmt.Println(foo.Item)
}
// => 3
// => 3
// => 3
// 中身が同じ、なぜ?
なぜか中身がすべておなじになる。
原因
range の第二引数のポインタを格納してしまっているため。
詳しくは foos
の中身をプリントしてみるとわかる
type Foo struct {
Item string
}
foo1 := Foo{"1"}
foo2 := Foo{"2"}
foo3 := Foo{"3"}
slice := []Foo{foo1, foo2, foo3}
var foos []*Foo
for _, foo := range slice {
foos = append(foos, &foo)
}
fmt.Println(foos)
// => [0x14000010230 0x14000010230 0x14000010230]
同じポインタが3つ格納されている。
これはどこで格納されたかと言うと、 繰り返し処理のときに range の左辺の変数のポインタが格納されている。
type Foo struct {
Item string
}
foo1 := Foo{"1"}
foo2 := Foo{"2"}
foo3 := Foo{"3"}
slice := []Foo{foo1, foo2, foo3}
var foos []*Foo
for _, foo := range slice {
foos = append(foos, &foo) // <= ここで変数 foo のポインタを参照し、foos に追加している
}
- range 文で何回繰り返そうと、左辺の変数は一度のみしか定義されない(同じ変数には破壊的代入を繰り返す)
- そのため、ブロック内の
&foo
は常に同じポインタ値を返す - 3回の繰り返しのうち、すべて同じポインタが格納される
-
foos
はすべて同じポインタ値を格納しているため、参照時にすべて同じ変数を見る - 3回目で
foo
にFoo{”3”}
が代入されるため、すべてFoo{"3"}
を見る
対策
for ブロックの中で、左辺の変数のポインタを取得しない。