読者です 読者をやめる 読者になる 読者になる

Goのポインタにハマったので備忘録

github.com


こんなコードがあります

package main

import "log"

var (
    yy []Hoge
    zz map[string]*Hoge = make(map[string]*Hoge)
)

type Hoge struct {
    a int
    b int
}

func main() {
    d1 := Hoge{}
    d1.a = 10
    d1.b = -20
    zz["d1"] = &d1
    yy = append(yy, d1)

    d2 := Hoge{}
    d2.a = 5
    d2.b = -8
    zz["d2"] = &d2
    yy = append(yy, d2)

    log.Println("yy[0] = ", yy[0], "*zz[d1] = ", *zz["d1"])
    log.Println("yy[1] = ", yy[1], "*zz[d2] = ", *zz["d2"])

    yy[0].a = -9
    yy[0].b = 888
    yy[1].a = -6
    yy[1].b = 444

    // zzにも変更が反映されると思い込んでた
    log.Println("yy[0] = ", yy[0], "*zz[d1] = ", *zz["d1"])
    log.Println("yy[1] = ", yy[1], "*zz[d2] = ", *zz["d2"])
}

コメントに書いたとおりなんですが、実行すると

2017/03/31 12:57:59 yy[0] =  {10 -20} *zz[d1] =  {10 -20}
2017/03/31 12:57:59 yy[1] =  {5   -8} *zz[d2] =  {5   -8}
2017/03/31 12:57:59 yy[0] =  {-9 888} *zz[d1] =  {10 -20} # yyへの変更がzzには
2017/03/31 12:57:59 yy[1] =  {-6 444} *zz[d2] =  {5   -8} # 反映されてくれない

途中でyyに与えた変更がzz側にも波及することを期待してたのですが、zzに保持されてるポインタは冒頭で定義した一時変数(のつもり)のd1/d2のポインタなのでソチラ側への変更が反映されてる、と。(しかも、d1/d2をzzが参照してるからGCで回収されないはず。。。)

期待通りに動かそうと思うと、格納してるポインタをd1/d2のものからyy[0]/yy[1]のものに直せば良いと言えば良いのですが

func main() {
    d1 := Hoge{}
    d1.a = 10
    d1.b = -20
    yy = append(yy, d1)

    d2 := Hoge{}
    d2.a = 5
    d2.b = -8
    yy = append(yy, d2)

    zz["d1"] = &yy[0]
    zz["d2"] = &yy[1]

    log.Println("yy[0] = ", yy[0], "*zz[d1] = ", *zz["d1"])
    log.Println("yy[1] = ", yy[1], "*zz[d2] = ", *zz["d2"])

    yy[0].a = -9
    yy[0].b = 888
    yy[1].a = -6
    yy[1].b = 444

    // これなら反映される
    log.Println("yy[0] = ", yy[0], "*zz[d1] = ", *zz["d1"])
    log.Println("yy[1] = ", yy[1], "*zz[d2] = ", *zz["d2"])
}

という感じにすると

2017/03/31 13:04:14 yy[0] =  {10 -20} *zz[d1] =  {10 -20}
2017/03/31 13:04:14 yy[1] =  {5   -8} *zz[d2] =  {5   -8}
2017/03/31 13:04:14 yy[0] =  {-9 888} *zz[d1] =  {-9 888} # 反映されてる
2017/03/31 13:04:14 yy[1] =  {-6 444} *zz[d2] =  {-6 444}

ともあれ、コレを書いていて気づきましたが、[]Hogeで管理してるのがそもそもアレで、最初からmap[string]*Hogeで管理しとけば良かっただけですね、コレ。影響範囲が広いので今から全部直すのは現実的じゃないなぁ(それは全然別の話ですが・・・)