Ответ
Операторы defer
в Go выполняются в порядке LIFO (Last-In, First-Out) — «последним пришел, первым вышел». Это означает, что самый последний defer
в коде функции выполнится первым, а самый первый — последним.
Каждый вызов defer
помещает функцию и ее аргументы в стек. Когда родительская функция завершает свое выполнение (через return
или панику), отложенные вызовы извлекаются из этого стека и выполняются по очереди.
Простой пример
package main
import "fmt"
func main() {
fmt.Println("Начало функции")
defer fmt.Println("Выполнится последним (1)")
defer fmt.Println("Выполнится вторым (2)")
defer fmt.Println("Выполнится первым (3)")
fmt.Println("Конец функции")
}
Вывод:
Начало функции
Конец функции
Выполнится первым (3)
Выполнится вторым (2)
Выполнится последним (1)
Важные нюансы
-
Вычисление аргументов: Аргументы для отложенной функции вычисляются в момент вызова
defer
, а не в момент ее выполнения.func printArg() { i := 0 defer fmt.Println("Значение i:", i) // i здесь равно 0 i++ fmt.Println("Функция завершается") } // Вывод: // Функция завершается // Значение i: 0
-
Взаимодействие с именованными возвращаемыми значениями:
defer
может читать и изменять именованные возвращаемые значения функции. Это полезно для обработки ошибок или модификации результата перед возвратом.func getNumber() (n int) { // n - именованное возвращаемое значение defer func() { n = n * 2 // Модифицируем n перед возвратом }() return 5 // 1. n присваивается 5, 2. выполняется defer, 3. возвращается n } // getNumber() вернет 10
-
Основное применение:
defer
идеально подходит для освобождения ресурсов, таких как закрытие файлов, разблокировка мьютексов или закрытие сетевых соединений, гарантируя их выполнение независимо от того, как завершилась функция (успешно или с паникой).