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

Как golang "отложить" параметр закрытия захвата?

Вот мой код (запустить):

package main

import "fmt"

func main() {
    var whatever [5]struct{}

    for i := range whatever {
        fmt.Println(i)
    } // part 1

    for i := range whatever {
        defer func() { fmt.Println(i) }()
    } // part 2

    for i := range whatever {
        defer func(n int) { fmt.Println(n) }(i)
    } // part 3
}

Вывод:

0 1 2 3 4 4 3 2 1 0 4 4 4 4 4

Вопрос: Какая разница между частью 2 и частью 3? Почему часть 2 выводит "44444" вместо "43210"?

4b9b3361

Ответ 1

Закрытие "части 2" фиксирует переменную "i" . Когда код в закрытии (позже) выполняется, переменная "i" имеет значение, которое оно имело в последней итерации оператора диапазона, т.е. '4'. Следовательно,

4 4 4 4 4

часть вывода.

"Часть 3" не фиксирует никаких внешних переменных при ее закрытии. Как говорят характеристики:

Каждый раз, когда выполняется оператор defer, значение функции и параметры для вызова оцениваются как обычно и сохраняются заново, но фактическая функция не вызывается.

Таким образом, каждый из вызовов отложенной функции имеет другое значение параметра "n". Это значение переменной "i" в момент выполнения инструкции defer. Следовательно,

4 3 2 1 0

часть вывода, потому что:

... отложенные вызовы выполняются в порядке LIFO непосредственно перед возвратом окружающей функции...


Ключевым моментом является то, что 'f()' в 'defer f()' не выполняется, когда оператор defer выполняет

но

выражение 'e' в 'defer f (e)' вычисляется, когда выполняется оператор defer.