経緯
- Golangのsliceに要素を追加する組み込み関数
append
さんの動作がおかしい!(誤解) - 何故か同じ要素が無限にコピーされたsliceが生成される!(誤解of誤解)
という記事を投稿したところコメントにて秒速でご指摘をいただきました。
常々心がけているこういう場合の99.9%は使い方が悪いだけ
というのが今回も例外でない結果になりました…。
間違っているソース
Playgroundはこちら: https://play.golang.org/p/jnm_Cp0yIBa
package main
import (
"encoding/json"
"fmt"
)
type Hoge struct {
ID int
}
func main() {
// 値のsliceを作る
hoges := []Hoge{}
for i := 0; i < 5; i++ {
hoges = append(hoges, Hoge{ID: i})
}
// 値のsliceをJSONで出力
JSONPrint(hoges)
// 要素の参照でできたsliceを作る
hogesRef := []*Hoge{}
for _, h := range hoges {
hogesRef = append(hogesRef, &h)
}
// 参照のsliceをJSONで出力
JSONPrint(hogesRef)
}
func JSONPrint(v interface{}) {
b, _ := json.Marshal(v)
fmt.Println(string(b))
}
出力結果
[{"ID":0},{"ID":1},{"ID":2},{"ID":3},{"ID":4}]
[{"ID":4},{"ID":4},{"ID":4},{"ID":4},{"ID":4}]
記事投稿前のぼく「( •̀ㅁ•́;) え、なんで」
今となっては当たり前だろという話です…。
勘違いポイント
参照のsliceを作る時にfor文で range
を使っていますが、このh
は他の言語と同様使い回しです。
for _, h := range hoges {
hogesRef = append(hogesRef, &h)
}
この&h
って、h
のポインタなんですよね…。hoges
の要素(いうなればhoges[i]
)へのポインタではないんです。それを知らずにh
へのポインタをappendしまくってるわけです。そして、for文で最後に来るhoges
の要素がh
に与えられ、それまでappendされた&h
はすべて最後の要素を指すということに…。
勝手に下記のようになると勘違いしていました。完全に理解不足です…。
for i := range hoges {
hogesRef = append(hogesRef, &hoges[i])
}
この修正で意図した動作を確認できました。いやはやアホすぎる
修正されたソースのPlaygroundはこちら: https://play.golang.org/p/KOifPTYN1jE
さいごに
コメントいただいている通りそもそもfor文、ポインタの理解不足が原因でしたので記事は修正されています。
あまりにも恥ずかしい初版はどうぞ履歴をご覧ください。
zetamatta様、ありがとうございます。大変勉強になりました。