Ваша интуиция верна: go повторно использует один и тот же адрес для значений итерации , поэтому нет гарантии, что значение, на которое указывает dir
, когда анонимная функция, добавленная к rmdirs
, имеет то же самоезначение, которое оно имело при создании функции и первом захвате dir
. Точная формулировка в спецификации :
Переменные итерации могут быть объявлены с помощью предложения range, используя форму краткого объявления переменных (: =).В этом случае их типам присваиваются типы соответствующих значений итерации, а их область действия является блоком оператора for; они повторно используются в каждой итерации .Если переменные итерации объявлены вне оператора for, после выполнения их значения будут такими же, как и в последней итерации.
(выделено мной).Для дальнейшей демонстрации ниже приведена упрощенная версия того, что пытается сделать ваш код:
var rmdirs []func()
tempDirs := []string{"one", "two", "three", "four"}
for _, dir := range tempDirs {
fmt.Printf("dir=%v, *dir=%p\n", dir, &dir)
rmdirs = append(rmdirs, func() {
fmt.Printf("dir=%v, *dir=%p\n", dir, &dir)
})
}
fmt.Println("---")
for _, f := range rmdirs {
f()
}
При запуске выдается следующий вывод:
dir=one, *dir=0x40e128
dir=two, *dir=0x40e128
dir=three, *dir=0x40e128
dir=four, *dir=0x40e128
---
dir=four, *dir=0x40e128
dir=four, *dir=0x40e128
dir=four, *dir=0x40e128
dir=four, *dir=0x40e128
Ссылка на игровую площадку: https://play.golang.org/p/_rS8Eq9qShM
Как видите, один и тот же адрес повторно используется в каждой итерации цикла.Каждая итерация анонимной функции просматривает один и тот же адрес, поэтому все они выводят одно и то же значение.
Правильный способ обработки подобных ситуаций, как упоминалось в книге, на которую вы ссылаетесь, заключается в определении новой переменнойвнутри цикла, копируя значение итерации в него и передавая его анонимной функции, например, так:
var rmdirs []func()
tempDirs := []string{"one", "two", "three", "four"}
for _, d := range tempDirs {
dir := d
fmt.Printf("dir=%v, *dir=%p\n", dir, &dir)
rmdirs = append(rmdirs, func() {
fmt.Printf("dir=%v, *dir=%p\n", dir, &dir)
})
}
fmt.Println("---")
for _, f := range rmdirs {
f()
}
Это дает ожидаемый результат:
dir=one, *dir=0x40e128
dir=two, *dir=0x40e150
dir=three, *dir=0x40e168
dir=four, *dir=0x40e180
---
dir=one, *dir=0x40e128
dir=two, *dir=0x40e150
dir=three, *dir=0x40e168
dir=four, *dir=0x40e180
Ссылка Playground: https://play.golang.org/p/Ao6fC9i2DsG