Голанское поведение отсрочки
Эффективный переход
заявляет следующее относительно отсрочки:
Аргументы отложенной функции (включая приемник, если функция является методом) оцениваются при выполнении отсрочки, а не при выполнении вызова. Кроме того, чтобы избежать беспокойства о переменных, изменяющих значения по мере выполнения функции, это означает, что один отложенный сайт может отложить выполнение нескольких функций. Вот глупый пример.
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
Отложенные функции выполняются в порядке LIFO, поэтому этот код вызывает печать 4 3 2 1 0
, когда функция возвращается.
Этот пример меня смущает. Если параметры оцениваются, когда выполняется запрос отсрочки, то отбойщики в цикле for должны печатать 5 5 5 5 5
, так как вызовы будут вызываться, когда цикл for завершается, и в это время i
будет равно 5. Оценка отступников на конец цикла for, таким образом, приведет к 5 для всех вызовов.
Я что-то пропустил?
Ответы
Ответ 1
Это кажется последовательным (см. также " Defer, Panic и Recover" )
Отложенные вызовы функций выполняются в порядке "Последний вход в первый выход" после возвращения внешней функции.
Эта функция печатает "3210":
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
Последний вызов, когда оценивается defer
, означает i=3
, предыдущее значение означает i=2
и т.д.
Спецификация Golang:
Каждый раз, когда выполняется оператор defer
", значение функции и параметры для вызова оцениваются как обычные и сохраняются заново, но фактическое тело функции не выполняется.
defers
будет вызываться, когда func заканчивается
да, но их аргументы оцениваются раньше, пока цикл запущен.
У вас более сложный отложенный случай в Как golang "отложить" параметр закрытия захвата? "при использовании с закрытием ( function literal), как описано в разделе "Зачем добавлять "()
" после закрытия тела в Голанге? ".
Ответ 2
Немного ниже, spec также явно говорит, что параметры оцениваются во время выполнения оператора defer
, а не при возврате/панике, когда отложенная функция фактически называется:
Каждый раз, когда выполняется оператор defer, значение функции и параметры для вызова оцениваются как обычно и сохраняются заново, но фактическое тело функции не выполняется.
И да, это определенно может ввести в заблуждение, что параметры оцениваются за один раз, а тело функции работает на другом. Я был пойман этим.
Ответ 3
Я думаю, что ваше замешательство - это то, что означают фразы "отсрочка" и "вызов". Я считаю, что "отсрочка" выполняется, когда поток управления достигает линии, начинающейся с defer
, т.е. Это происходит пять раз внутри цикла. Напротив, "вызов выполняется", когда выполняется fmt.Printf("%d ", i)
, т.е. Когда возвращается окружающая функция.
Если эта интерпретация верна, ваше утверждение ", поскольку вызывающие вызовы будут вызваны, когда конец цикла завершается", неверно (printf
будет вызываться после цикла, но defer
вызывается внутри), и все в соответствии с поведением, объясненным в других ответах.