Golang for循环局部变量问题
几乎世界上每个 Golang 程序员都踩过一遍 for 循环变量的坑
举栗子🌰
func main() {
var ids []*int
for i := 0; i < 10; i++ {
ids = append(ids, &i)
}
for _, item := range ids {
println(*item)
}
}
可以在go.dev/play中运行
答案是:打印出来的全是 10。
这个结果实在离谱。原因是因为在目前 Go 的设计中,for 中循环变量的定义是 per loop 而非 per iteration。也就是整个 for 循环期间,变量 i
只会有一个。以上代码等价于:
var ids []*int
var i int
// i实例化以后都是同一个内存地址
for i = 0; i < 10; i++ {
ids = append(ids, &i)
}
同样的问题在闭包使用循环变量时也存在,代码如下:
var prints []func()
for _, v := range []int{1, 2, 3} {
prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
print()
}
根据上面的经验,闭包 func 中 fmt.Println(v)
,捕获到的 v
都是同一个变量。因此打印出来的都是 3。
解决方法
引用局部变量
在目前的 go 版本中,正常来说我们会这么解决:
var ids []*int
for i := 0; i < 10; i++ {
i := i // 局部变量
ids = append(ids, &i)
}
定义一个新的局部变量, 这样无论闭包还是指针,每次迭代时所引用的内存都不一样了。
官方修复
目前使用go.1.22+版本已修复
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Hunter
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果