LoginSignup
1
0

More than 3 years have passed since last update.

Go言語のループ中でポインタ値を更新するときの落とし穴

Last updated at Posted at 2020-04-27

問題

スライスtsをイテレートして、条件(ここでは単純にt == "c")に合う値のポインタを見つけたいとします。

The Go Playground

func main() {
    ts := []string{"a", "b", "c", "d", "e"}
    p := &ts[0] // 初期値は"a"とする。
    for _, t := range ts {
        if t == "c" {
            p = &t
        }
        fmt.Println("current p = " + *p)
    }
    fmt.Println("final p = " + *p)
}

これで、

current p = a
current p = a
current p = c
current p = c
current p = c
final p = c

と出力されると思いましたか!?私は思いました。

残念ながら、こうなります。

current p = a
current p = a
current p = c
current p = d
current p = e
final p = e

ループごとにtを定義しているのかと思い込んでいましたが、どうやらイテレートを通して同じ変数らしく、書き換わっていってしまっているのでしょう。

解決

コピーが発生しますが、ポインタではなく値を保持するか、

func main() {
    ts := []string{"a", "b", "c", "d", "e"}
    p := ts[0] // 初期値は"a"とする。
    for _, t := range ts {
        if t == "c" {
            p = t
        }
        fmt.Println("current p = " + p)
    }
    fmt.Println("final p = " + p)
}

元のスライスへの参照を保持するか、

func main() {
    ts := []string{"a", "b", "c", "d", "e"}
    p := &ts[0] // 初期値は"a"とする。
    for idx, t := range ts {
        if t == "c" {
            p = &ts[idx]
        }
        fmt.Println("current p = " + *p)
    }
    fmt.Println("final p = " + *p)
}

くらいしかないですかね。

2020-04-30 追記

公式ドキュメントに言及がありました。

これに従うと、

func main() {
    ts := []string{"a", "b", "c", "d", "e"}
    p := &ts[0] // 初期値は"a"とする。
    for _, t := range ts {
        t := t
        if t == "c" {
            p = &t
        }
        fmt.Println("current p = " + *p)
    }
    fmt.Println("final p = " + *p)
}

でも良いということですね。
t := tってすごい違和感ありますけどね・・・。

1
0
0

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