問題
スライスts
をイテレートして、条件(ここでは単純にt == "c"
)に合う値のポインタを見つけたいとします。
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
ってすごい違和感ありますけどね・・・。