LoginSignup
1
0

More than 5 years have passed since last update.

ポインタ初心者がfor文の使い方を間違えた話

Last updated at Posted at 2018-09-19

経緯

  • 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様、ありがとうございます。大変勉強になりました。

1
0
4

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
1
0