Что выведет этот фрагмент с двумя параметризованными отложенными замыканиями и в каком порядке?
Прочитайте фрагмент ниже. Два отложенных замыкания принимают параметр val, а a переприсваивается между двумя операторами defer.
func deferExample() int {
a := 10
defer func(val int) {
fmt.Println("first:", val)
}(a)
a = 20
defer func(val int) {
fmt.Println("second:", val)
}(a)
a = 30
fmt.Println("exiting:", a)
return a
}
Определите вывод.
Выведет exiting: 30, second: 20, first: 10. Аргументы отложенного вызова вычисляются в момент оператора defer, поэтому (a) фиксирует значение a — 10, затем 20 — и последующее a = 30 их не меняет. Отложенные вызовы выполняются в порядке LIFO.
- ✗Полагать, что параметр отложенного замыкания вычисляется при выполнении вызова, а не на операторе
defer - ✗Ожидать отложенные вызовы в порядке регистрации, печатая
firstпередsecond - ✗Считать, что финальное
a = 30перекрывает значения, уже зафиксированные в отложенных вызовах
- →Как изменился бы вывод, если бы замыкания читали
aнапрямую вместо параметраval? - →Почему
exiting: 30печатается раньше всех отложенных строк?
Что выведет код?
func deferExample() int {
a := 10
defer func(val int) {
fmt.Println("first:", val)
}(a)
a = 20
defer func(val int) {
fmt.Println("second:", val)
}(a)
a = 30
fmt.Println("exiting:", a)
return a
}
Вывод
exiting: 30
second: 20
first: 10
Почему именно так
Главная тонкость — аргументы отложенного вызова вычисляются в момент оператора defer, даже если вызов является замыканием с параметрами. Выражение (a) в конце каждого defer — это обычная передача аргумента, и оно вычисляется сразу:
- первый
deferфиксируетval = 10(тогдаa == 10); - второй
deferфиксируетval = 20(к этому моментуa == 20).
Последующее a = 30 уже не влияет на захваченные значения — они скопированы и лежат вместе с отложенными вызовами.
Сначала выполняется тело функции, поэтому exiting: 30 печатается первым. Затем, при возврате, отложенные вызовы запускаются в порядке LIFO (последний зарегистрированный — первым), поэтому second: 20 идёт раньше first: 10.