В какой точке выполнения recover действительно перехватывает панику в Go?
recover перехватывает панику только при прямом вызове из функции, зарегистрированной через defer, пока паника распространяется в той же горутине. Вызов recover из обычной функции, вызванной отложенной функцией, не восстанавливает выполнение.
После успешного восстановления функция, содержащая отложенный обработчик, завершается; выполнение не возвращается к инструкции, вызвавшей panic.
Модель panic/recover предназначена для исключительных ситуаций, когда продолжать обычное выполнение невозможно или нежелательно. Штатные ожидаемые ошибки в Go обычно возвращают явно, а panic и recover позволяют обозначить границу аварийного завершения, например на уровне обработчика запроса.
Связка с defer обеспечивает выполнение завершающей логики при раскрутке стека: освобождение ресурсов, запись диагностики и, при необходимости, преобразование паники в контролируемый результат.
Недостаточно просто вызвать recover: важны момент вызова и место, где он выполняется. Если обработчик вызовет recover слишком поздно, в другой горутине или через промежуточную функцию, паника продолжит распространяться и может завершить горутину с аварией.
Ошибочная попытка продолжить работу после паники также опасна. Состояние функции, вызвавшей panic, уже не считается обычной точкой продолжения, поэтому код после неё не возобновляется.
Во время паники Go раскручивает стек вызовов текущей горутины и выполняет отложенные функции. Если одна из них непосредственно вызывает recover, паника останавливается, а recover возвращает переданное в panic значение.
Здесь анонимная функция является непосредственно отложенным обработчиком, поэтому паника перехватывается. После завершения этой функции guarded возвращает управление вызывающему коду, и main печатает after.
Критично различать прямой и косвенный вызов. Если отложенная функция вызывает другую функцию, а уже та вызывает recover, восстановление не произойдёт: recover должен быть вызван непосредственно отложенной функцией, выполняемой в процессе обработки паники.
Паника локальна для горутины. Обработчик в одной горутине не может перехватить панику другой; для каждой горутины нужна собственная граница с defer и recover.
Использовать recover следует на чётко определённой границе, например в адаптере HTTP-обработчиков или в рабочем цикле фоновой горутины. Не стоит повсеместно скрывать паники: это может замаскировать ошибку программы и привести к продолжению работы с повреждёнными предположениями.
В серверном приложении пользовательский обработчик может вызвать panic, а внешний HTTP-слой должен вернуть контролируемый ответ и записать стек вызовов.
Вариант без recover прост, но одна паника может прервать обработку запроса или завершить рабочую горутину. Вариант с recover внутри каждого обработчика даёт локальный контроль, однако дублирует код и повышает риск забыть обработчик.
Практичнее разместить единый отложенный обработчик в middleware или в функции запуска рабочей горутины. Он перехватывает панику на границе подсистемы, логирует значение и стек, после чего возвращает безопасный результат; при этом ошибки, которые ожидаемо обрабатываются бизнес-логикой, по-прежнему передаются через error.
recover в обычной функции, если она была вызвана из deferred-обработчика?Нет. Вызов должен быть непосредственным: отложенная функция сама должна выполнить recover. Если deferred-функция вызывает промежуточную функцию, а та вызывает recover, результатом будет nil, и паника не будет остановлена.
recover в одной горутине перехватить панику из другой?Нет. Раскрутка стека и выполнение отложенных функций происходят внутри горутины, где возникла паника. Поэтому запуск работы в отдельной горутине требует отдельного defer с recover внутри этой горутины.
panic после успешного recover?Нет. После восстановления завершается функция, в которой сработал отложенный обработчик, а управление возвращается её вызывающему коду. Инструкции, расположенные после panic в той же функции, не выполняются.