在GO语言匿名函数学习中我已经阐明了关于匿名函数对外层函数的变量的引用,在这里需要再次进行强调:匿名函数对外层变量的使用和修改是修改的外层变量的引用,由于在循环中变量的地址是相同的,导致匿名函数使用的变量可能会是同一个。
var rmdirs []func()
for _,d := range tempDirs() {
dir := d // 注意,这一行是必须的
os.MkdirAll(dir,0755) // 也创建父目录
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir)
})
}
// ...这里做一些处理...
for _,rmdir := range rmdirs {
rmdir() //清理
}
可以看到第一个for循环中:dir := d,局部变量dir是循环变量d的一个副本,从而消除了rmdirs中是同一个变量引用的bug。
风险不仅存在使用range的for循环中。下面的例子中也存在物件见的捕获的索引变量i而导致的同样的问题。
var rmdirs []func()
dirs := tempDirs()
for i := 0; i < len(dirs); i++ {
os.MkdirAll(dirs[i], 0755) // ok
rmdirs = append(rmdirs, func() {
os.RemoveAll(dirs[i]) // 不正确
})
}
这两种捕获迭代变量导致的问题本质上是因为:在go语言中迭代变量使用同一个地址。
参考:《GO程序设计语言》