### 5.6.1. 警告：捕獲迭代變量

本節，將介紹Go詞法作用域的一個陷阱。請務必仔細的閲讀，弄清楚發生問題的原因。卽使是經驗豐富的程序員也會在這個問題上犯錯誤。

考慮這個樣一個問題：你被要求首先創建一些目録，再將目録刪除。在下面的例子中我們用函數值來完成刪除操作。下面的示例代碼需要引入os包。爲了使代碼簡單，我們忽略了所有的異常處理。

```Go
var rmdirs []func()
for _, d := range tempDirs() {
	dir := d // NOTE: necessary!
	os.MkdirAll(dir, 0755) // creates parent directories too
	rmdirs = append(rmdirs, func() {
		os.RemoveAll(dir)
	})
}
// ...do some work…
for _, rmdir := range rmdirs {
	rmdir() // clean up
}
```

你可能會感到睏惑，爲什麽要在循環體中用循環變量d賦值一個新的局部變量，而不是像下面的代碼一樣直接使用循環變量dir。需要註意，下面的代碼是錯誤的。

```go
var rmdirs []func()
for _, dir := range tempDirs() {
	os.MkdirAll(dir, 0755)
	rmdirs = append(rmdirs, func() {
		os.RemoveAll(dir) // NOTE: incorrect!
	})
}
```

問題的原因在於循環變量的作用域。在上面的程序中，for循環語句引入了新的詞法塊，循環變量dir在這個詞法塊中被聲明。在該循環中生成的所有函數值都共享相同的循環變量。需要註意，函數值中記録的是循環變量的內存地址，而不是循環變量某一時刻的值。以dir爲例，後續的迭代會不斷更新dir的值，當刪除操作執行時，for循環已完成，dir中存儲的值等於最後一次迭代的值。這意味着，每次對os.RemoveAll的調用刪除的都是相同的目録。

通常，爲了解決這個問題，我們會引入一個與循環變量同名的局部變量，作爲循環變量的副本。比如下面的變量dir，雖然這看起來很奇怪，但卻很有用。

```Go
for _, dir := range tempDirs() {
	dir := dir // declares inner dir, initialized to outer dir
	// ...
}
```

這個問題不僅存在基於range的循環，在下面的例子中，對循環變量i的使用也存在同樣的問題：

```Go
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]) // NOTE: incorrect!
	})
}
```

如果你使用go語句（第八章）或者defer語句（5.8節）會經常遇到此類問題。這不是go或defer本身導致的，而是因爲它們都會等待循環結束後，再執行函數值。
