Подтвердить что ты не робот

Голанское поведение отсрочки

Эффективный переход заявляет следующее относительно отсрочки:

Аргументы отложенной функции (включая приемник, если функция является методом) оцениваются при выполнении отсрочки, а не при выполнении вызова. Кроме того, чтобы избежать беспокойства о переменных, изменяющих значения по мере выполнения функции, это означает, что один отложенный сайт может отложить выполнение нескольких функций. Вот глупый пример.

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 для всех вызовов.

Я что-то пропустил?

4b9b3361

Ответ 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 вызывается внутри), и все в соответствии с поведением, объясненным в других ответах.