言葉で説明するのだるいので次のコードを見てください。
package main import ( "fmt" ) func main() { array := []int{1, 2, 3} // これだと &a が同じアドレスになってしまうことがある // (range は array[i] のコピーを返す) for i, a := range array { fmt.Printf("%d: %p\n", i, &a) } // こうするか fmt.Println("----------------------------") for i := range array { a := array[i] fmt.Printf("%d: %p\n", i, &a) } // こうしないといけない fmt.Println("----------------------------") for i := range array { fmt.Printf("%d: %p\n", i, &array[i]) } }
for i, a := range ... {
とした時に &a が毎回同じアドレスになってしまうことがある、ということです(結局言葉で説明…)。
あくまで range の2番目の返り値は配列の各要素へのポインタではなく値を取得したい場合に使うらしいです。
なので以下のコードはダメ。
package main import ( "fmt" ) type MyObj struct { id int } func main() { array := []MyObj{MyObj{id: 1}, MyObj{id: 2}, MyObj{id: 3}} // id >= 2 の要素だけ抜き出す filtered := filterGreaterThan1(array) // 出力すると自分の環境だと 3 が2回表示された for _, a := range filtered { fmt.Println(a.id) } } func filterGreaterThan1(array []MyObj) []*MyObj { filtered := make([]*MyObj, 0, len(array)) for _, a := range array { if a.id > 1 { // a は同じアドレスの可能性があるため &a を持ちまわると同じ参照を指してしまうことがある filtered = append(filtered, &a) } } return filtered }