Выполняется ли defer при панике в Go
Что проверяют
Знаете ли вы механику раскрутки стека в Go, а не только сам факт «да, выполняется». Следом почти всегда спрашивают про recover: где он работает, что возвращает функция после восстановления и что будет с паникой в другой горутине.
Как отвечать
Да, выполняется. При panic обычное выполнение функции останавливается, и рантайм раскручивает стек: в каждой функции по пути вызываются её отложенные вызовы в порядке LIFO. Если никто не восстановился, программа печатает сообщение со стеком и завершается с кодом 2.
Ключевые детали:
recover: срабатывает только при прямом вызове внутри отложенной функции. Вызванный из обычного кода или из вспомогательной функции, которую зовёт defer, он вернётnil;- возврат после восстановления: функция, где сработал
recover, завершается нормально. Если результаты именованные, defer может их выставить, напримерerr = fmt.Errorf("panic: %v", r), и вызывающий получит ошибку вместо падения; - аргументы defer: вычисляются в момент выполнения строки с
defer, а не при вызове отложенной функции; - горутины: паника раскручивает стек только своей горутины. Горутина без собственного
recoverроняет весь процесс, и defer вmainеё не спасёт.
Практический смысл: закрытие файлов, mu.Unlock(), откат транзакции через defer отработают и при панике. Так net/http не даёт одному упавшему обработчику положить сервер: паника перехватывается на уровне соединения и пишется в лог.
Хорошо добавить, что с Go 1.21 panic(nil) превращается в *runtime.PanicNilError, так что recover больше не возвращает nil для настоящей паники.
Где спотыкаются
Путают панику с os.Exit. os.Exit и log.Fatal, который его вызывает, завершают процесс сразу, и отложенные вызовы не выполняются вовсе.
Считают, что recover ловит всё. Фатальные ошибки рантайма, например конкурентная запись в map или дедлок всех горутин, не восстанавливаются.
Используют panic и recover как исключения для обычных ошибок. В Go ошибки возвращаются значением error, а паника остаётся для нарушенных инвариантов.