Как в Go можно обработать панику (panic)?

Ответ

В Go панику можно перехватить и обработать с помощью встроенной функции recover(). Ключевое правило: recover() работает только внутри отложенной функции (defer).

Если паника не обработана, выполнение программы немедленно прекращается.

Механизм работы

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

Классический пример — защита HTTP-обработчика:

func safeHandler(w http.ResponseWriter, r *http.Request) {
    // defer-функция для перехвата паники
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered from panic: %vn%s", r, debug.Stack())
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        }
    }()

    // Код, который может вызвать панику
    potentiallyPanicFunction()
    fmt.Fprintln(w, "Success")
}

func potentiallyPanicFunction() {
    panic("something went terribly wrong!")
}

Ключевые моменты:

  • Паника в горутине: Паника в любой горутине (кроме main) приводит к завершению всей программы, если она не обработана с помощью recover внутри этой же горутины.
  • panic vs error: В идиоматическом Go панику следует использовать только для действительно исключительных ситуаций, указывающих на ошибку в программе (например, выход за пределы массива), а не для ожидаемых ошибок (отсутствие файла, сетевые проблемы). Для обработки ожидаемых ошибок всегда используйте тип error.