История вопроса:
Операторы defer, panic и recover — важные механизмы управления потоком выполнения в Go. Оператор defer используется для отложенного выполнения функций, panic инициирует ошибку (аварийное завершение), а recover позволяет перехватить аварию и продолжить выполнение.
Проблема:
Без грамотного использования этих средств сложно корректно финализировать ресурсы и реализовать отказоустойчивость. Непредсказуемое завершение работы функции, неосвобожденные ресурсы и неуправляемый "выход" приложения при ошибках — частые проблемы при неправильном подходе.
Решение:
Правильное применение defer обеспечивает безопасное освобождение ресурсов даже в случае ошибок. panic — механизм аварийного завершения при нештатных ситуациях, который должен использоваться только для truly exceptional случаев. recover дает возможность "спасти" выполнение внутри отложенной функции.
Пример кода:
func riskyFunction() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in riskyFunction:", r) } }() fmt.Println("Doing some work...") panic("something bad happened!") } func main() { riskyFunction() fmt.Println("After riskyFunction") }
Ключевые особенности:
defer всегда вызывается в обратном порядке до выхода из функции. Даже при panic.recover работает ТОЛЬКО внутри отложенных функций.panic прерывает выполнение текущей горутины; если не пойман через recover — завершает процесс.Почему recover не работает вне defer внутри той же функции, где произошёл panic?
recover возвращает ненулевое значение (то есть перехватывает панику) только если вызывается из defer-функции, вложенной в ту же функцию, в которой произошла panic. Если вызвать recover напрямую, оно всегда возвращает nil.
Пример кода:
func f() { panic("fail!") r := recover() // не работает! }
Можно ли вызвать defer после panic и он выполнится?
Нет. После panic все уже зарегистрированные defer-вызовы выполняются, но новые defer после panic не будут вызваны, так как выполнение функции уже "схлопывается".
Можно ли восстановиться (recover) из panic, произошедшей в другой горутине?
Нет, recover работает только для паники в текущей горутине. Если другая горутина паникует и вызов recover не произошёл в этой же горутине — приложение завершится.
В проекте все ошибки кидались через panic, а обработка recovery была только в главной функции. Это приводило к тому, что ресурсы не закрывались, часть данных терялась, а логи были нечитаемы.
Плюсы:
Минусы:
В каждом критическом участке использовался defer для освобождения ресурсов, panic применялась только для truly exceptional ситуаций, а recover — для изоляции аварий в некритических частях. При этом логировались все детали ошибки.
Плюсы:
Минусы: