ПрограммированиеGo разработчик

Объясните, как работает defer в Go и когда порядок вызовов может стать неожиданностью. Какие подводные камни встречаются на практике?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Ключевое слово defer в Go откладывает выполнение указанных функций до выхода из окружающей функции — функции накапливаются в стек и выполняются в обратном порядке (LIFO). Это часто используют для освобождения ресурсов (файлы, mutex, соединения).

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

func test() { for i := 0; i < 3; i++ { defer fmt.Println(i) // Распечатает: 2, 1, 0 } }

Вопрос с подвохом.

Что выведет следующий код?

func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) } }

Ответ:

Это выведет:

2
1
0

Потому что при каждом цикле аргумент i вычисляется немедленно (в момент defer), и все значения хранятся в стеке defer-ов.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

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


История

В data pipeline произошло забывание — в цикле использовали defer для закрытия connect-ов, но фактически они закрылись только после завершения всей функции, а не после каждой итерации. Это привело к исчерпанию ресурсов.


История

В логгере использовали defer с анонимной функцией, ожидая, что аргумент вычисляется во время вызова. Из-за этого лог в конце содержал неактуальную информацию, так как значения захватывались раньше.