Что не так с этим defer?

Пользователь

от lamar , в категории: Golang , 4 месяца назад

Что не так с этим defer?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main


import "fmt"


func main() {
	for i := 0; i < 3; i++ {
		defer fmt.Println("Index:", i)
	}
}

"Почему вывод не 2, 1, 0? Что-то не так с defer или с i?"

Facebook Vk Ok Twitter LinkedIn Telegram Whatsapp

1 ответ

Пользователь

от lori_jast , месяц назад

@lamar 

В приведённом вами коде выгрузка откладываемых вызовов происходит, как ожидается, но результат может не совпадать с вашими предположениями из-за того, как работает defer в сочетании с циклами.


При использовании defer, функции или инструкции откладываются до тех пор, пока текущая функция не завершится. В вашем случае это функции main. Однако есть одна особенность: defer фиксирует аргументы функции на момент, когда defer вызывается. Это означает, что для всех вызовов defer будет зафиксировано одно и то же значение переменной i на последней итерации цикла.


В вашем цикле for i := 0; i < 3; i++ при каждой итерации вызывается defer fmt.Println("Index:", i). Однако значение i на момент завершения цикла будет равно 3. Это значение и будет использоваться во всех трех отложенных вызовах fmt.Println.


Таким образом, ваш код выведет:

1
2
3
Index: 3
Index: 3
Index: 3


Если вы хотите, чтобы печатались значения i в обратном порядке 2, 1, 0, можно использовать замыкание, чтобы зафиксировать текущее значение i на каждой итерации:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		i := i // создаем новую переменную i в текущей области видимости
		defer fmt.Println("Index:", i)
	}
}


Теперь вывод будет следующим:

1
2
3
Index: 2
Index: 1
Index: 0


Каждая итерация сохраняет текущее значение i на момент создания defer, что соответствует вашим ожиданиям.