Как правильно использовать `recover` для обработки паники в Go?

Ответ

В Go recover() — это встроенная функция, которая позволяет перехватить панику и восстановить нормальное выполнение программы. Её использование подчиняется строгим правилам.

Основной механизм:

  1. recover() работает только внутри отложенной функции, вызванной через defer.
  2. Если текущая горутина находится в состоянии паники, вызов recover() остановит панику и вернет значение, которое было передано в panic().
  3. Если паники нет, recover() вернет nil.

Пример правильного использования:

func safeCall() {
    // Функция defer выполняется при выходе из safeCall, в том числе при панике.
    defer func() {
        // recover() перехватывает панику.
        if r := recover(); r != nil {
            fmt.Println("Произошла паника, но мы её обработали:", r)
        }
    }()

    fmt.Println("Вызываем панику...")
    panic("произошла критическая ошибка")
    fmt.Println("Эта строка никогда не выполнится")
}

Ключевые моменты и лучшие практики:

  • panic vs error: В Go для обработки ожидаемых ошибок (например, "файл не найден") принято использовать возвращаемые значения типа error. Паника (panic) предназначена для действительно исключительных ситуаций, которые указывают на ошибку в программе (например, выход за пределы массива), и которые программа не может штатно обработать.
  • Когда использовать recover: Основное применение — на верхнем уровне горутины, чтобы предотвратить падение всего приложения из-за ошибки в одной из них. Например, в HTTP-сервере можно обернуть обработчик каждого запроса в defer с recover, чтобы паника в одном запросе не "убила" весь сервер.
  • Не злоупотребляйте: Не используйте panic/recover для обычной логики управления потоком выполнения. Это делает код сложным для понимания и поддержки.