defer откладывает вызов до выхода из окружающей функции. Функция и аргументы вызова вычисляются сразу, а отложенные вызовы выполняются в обратном порядке. Механизм удобен для освобождения ресурсов; он работает при обычном возврате и раскрутке panic, но не при os.Exit или принудительном завершении процесса.
Что означает ключевое слово defer в Go?
Когда выполняется отложенный вызов, почему важен порядок LIFO и почему defer не является безусловной гарантией очистки при любом завершении процесса.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
defer регистрирует вызов, который выполнится перед возвратом окружающей функции. Это не выход из ближайшего блока или итерации цикла. Несколько вызовов выполняются в порядке LIFO: последним зарегистрирован — первым выполнен. Правила определены в спецификации Go.
func example() {
value := 1
defer fmt.Println(value)
value = 2
fmt.Println(value)
}
Функция напечатает сначала 2, затем 1: аргумент отложенного вызова вычислен при выполнении defer. Замыкание, читающее переменную позднее, может дать другой результат.
Обычный сценарий — открыть файл, проверить ошибку и только после успешного открытия зарегистрировать defer file.Close(). В цикле внутри долгоживущей функции такие закрытия накапливаются; при необходимости ресурс обрабатывают в отдельной функции.
При панике отложенные вызовы выполняются во время раскрутки стека соответствующей горутины. Однако os.Exit завершает процесс без их выполнения. Ошибки освобождения ресурса тоже нельзя считать автоматически обработанными: например, ошибку закрытия записываемого файла иногда необходимо явно вернуть вызывающему коду.