> Что происходит с паниками в Go? (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: InDrive

Стек: Go

> Пример ответа

Короткий ответ

Паника в Go - это механизм аварийного завершения горутины при возникновении невосстановимой ошибки. Она останавливает выполнение текущей функции, разматывает стек вызовов, выполняя отложенные defer-функции, и, если паника не перехвачена через recover, завершает всю программу. Паники не распространяются между горутинами - каждая горутина обрабатывает свои паники независимо.

Подробное объяснение

Паника возникает через вызов panic(value) или автоматически при runtime-ошибках: index out of range, nil pointer dereference, assertion failure и т.д. Механизм работы:

  1. Вызов panic останавливает выполнение текущей функции.
  2. Выполняются все отложенные defer-функции в порядке LIFO (последний зарегистрированный - первый выполненный).
  3. Если в какой-то из defer-функций вызывается recover(), паника перехватывается, и управление возвращается в точку после defer - но только в той функции, где был вызван recover.
  4. Если recover не вызван, стек разматывается дальше, выполняя defer в вызывающих функциях.
  5. Если паника достигает вершины горутины - программа завершается с ненулевым кодом и печатает stack trace.

Важные особенности:

  • recover() возвращает значение, переданное в panic, и только если вызывается внутри defer - в другом контексте он возвращает nil.
  • После recover горутина продолжает выполнение с точки после defer, но не с места паники - функция, где произошла паника, считается завершённой.
  • Паника в одной горутине не влияет на другие горутины - они продолжают работать, но если паника не перехвачена, программа падает целиком.
  • panic(nil) в Go 1.21+ вызывает панику с *runtime.PanicNilError, а не просто nil - это исправление старого неоднозначного поведения.

На практике

Паники - это исключительный механизм для truly exceptional ситуаций: programmer errors, invariant violations, unrecoverable state corruption. Для обычных ошибок используйте возврат error - это идиоматичный подход в Go.

Паттерны использования:

  • Перехват паник на границах подсистем - например, в HTTP middleware или в main для логирования и graceful shutdown.
  • Преобразование паник в ошибки - в библиотечном коде, чтобы не "выстреливать" в пользователя библиотеки.
  • Восстановление после паник в горутинах - обёртка для go func() с defer recover() для логирования и предотвращения падения всего приложения.
  • Тестирование - проверка, что функция паникует, через assert.Panics или ручной defer recover.

Не используйте паники для контроля потока выполнения или для обработки ожидаемых ошибок - это антипаттерн.

Пример кода

GO
// Перехват паники в горутине
func safeGo(fn func()) {
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("recovered from panic: %v", r)
}
}()
fn()
}()
}
// Преобразование паники в ошибку
func safeCall(fn func() error) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
return fn()
}
// Пример с defer и recover
func example() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
panic("something went wrong")
fmt.Println("this line never executes")
}

Как отвечать на собеседовании

Начните с краткого определения, затем опишите механику: стек разматывается, defer выполняются, recover перехватывает. Подчеркните, что паника - это не замена ошибкам, а механизм для исключительных ситуаций. Упомяните, что паники не пересекают границы горутин - это частый вопрос на собеседованиях. Приведите пример из реальной практики: middleware для HTTP-сервера, который перехватывает паники и возвращает 500 вместо падения процесса.

Если спросят про panic(nil) - упомяните изменение в Go 1.21. Также стоит упомянуть, что recover работает только внутри defer, и что после перехвата паники выполнение продолжается с точки после defer, а не с места паники.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание разницы между ошибками и паниками - это ключевой момент.
  • Знание механики: порядок выполнения defer, LIFO, поведение recover.
  • Понимание изоляции горутин - паника в одной горутине не перехватывается в другой.
  • Практический опыт: когда уместно использовать panic/recover, а когда - нет.
  • Умение объяснить trade-off: паника упрощает код в редких случаях, но усложняет отладку и контроль ошибок.

Типичные ошибки

  • Утверждение, что recover можно вызвать вне defer - это не работает, вернёт nil.
  • Мнение, что паника в горутине убьёт только эту горутину - на самом деле убьёт всю программу, если не перехватить.
  • Использование паник для обработки обычных ошибок - это антипаттерн, интервьюер это сразу заметит.
  • Незнание, что после recover выполнение продолжается не с места паники, а после defer - это частая ошибка в рассуждениях.
  • Забывают упомянуть, что defer выполняется при панике - это базовое поведение, но его часто упускают.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью