defer recover() без обёртки не спасёт от паники
На собеседовании часто дают код, где recover() стоит вроде бы в правильном месте — внутри defer — но программа всё равно падает. Разбор показывает: recover работает только если он вызван напрямую внутри функции с defer, а не через ещё один уровень вызова.
func safeCall() { defer recover() panic("boom") }
func main() { safeCall() fmt.Println("ok") }
Здесь recover() вызван напрямую внутри defer — казалось бы, всё верно. Но есть разница между defer recover() и defer func() { recover() }(): в первом случае recover() вызывается в момент объявления defer, а не в момент паники — то есть он выполняется сразу, до паники, и возвращает nil, потому что паники ещё нет.
recover() имеет смысл только будучи вызванным ВНУТРИ функции, которая выполняется как отложенная (defer func() { ... }()), и именно в момент разворачивания стека после panic. Если написать просто defer recover() без обёртки в анонимную функцию, Go вычисляет аргументы defer сразу же в момент объявления — recover() срабатывает мгновенно, видит отсутствие активной паники и возвращает nil. Паника в safeCall() продолжает раскручивать стек необработанной, программа падает с сообщением panic: boom, а строка ok так и не печатается.
Правило для собеса: recover() ловит панику только изнутри именно той defer-функции, которая выполняется в момент паники — обёртка в closure обязательна.
Тренажёр: 1900+ вопросов и задач, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки