Ключевое слово 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 с анонимной функцией, ожидая, что аргумент вычисляется во время вызова. Из-за этого лог в конце содержал неактуальную информацию, так как значения захватывались раньше.