Работа со значениями в цикле в go
Я только изучаю golang, перехожу на него с java, и поведение в go цикла после java оказалось для меня немного неожиданным. Помогите, пожалуйста, разобраться, мне кажется, я запуталась. Почему в случае
funcs := []func(){}
for i := 0; i < 5; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}
funcs[0]()
получается 5, а не 0, а в случае
ids := []int{}
for i := 0; i < 5; i++ {
ids = append(ids, i)
}
fmt.Println(ids)
получается [0 1 2 3 4]?
Что будет, если у меня есть структура
type task struct {
id string
}
Что будет в слайсе result, если использовать его дальше? Только последний task1 или все, как в tasks?
func test(tasks []task ) []task{
var result = make([]task, 0)
for _, task1 = range tasks {
result = append(result , task1 )
}
return result
}
Ответы (1 шт):
Это достаточно известная ловушка языка Go, только она не в циклах, а в замыканиях. В первом примере у вас создается слайс, в него помещаются замыкания (функции), каждое из которых захватывает переменную из счетчика цикла. Здесь кроется нюанс - в Go до версии 1.22 переменная в замыкание захватывается по ссылке, то есть все элементы слайса захватили одно и то же значение.
Чтобы исправить ваш пример, надо явно указать, что переменная должна быть локальна в итерации цикла:
for i := 0; i < 5; i++ {
i := i
funcs = append(funcs, func() {
fmt.Println(i)
})
}
Теперь на каждой итерации цикла замыкание захватывает свою локальную переменную и они никак не связаны.
Циклы же работают без сюрпризов, как в остальных ваших примерах.
Эта милая особенность исправлена начиная с версии 1.22, который на момент написания ответа еще не вышел. Подробнее про это поведение и изменения в новой версии: