Что такое `panic` в Go и в каких случаях его стоит использовать?

Ответ

Паника (panic) — это встроенная функция в Go, которая останавливает обычный поток выполнения программы и начинает процесс "паники". Это механизм для обработки исключительных, невосстановимых ошибок, при которых дальнейшая работа программы невозможна или небезопасна.

Как работает механизм panic/recover:

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

Когда следует использовать panic:

  • Невосстановимые ошибки на старте: Например, невозможность подключиться к базе данных или прочитать критически важный конфигурационный файл при запуске приложения.
  • Ошибки программиста (нарушение инвариантов): Ситуации, которые не должны происходить в корректно работающей программе. Например, доступ к элементу среза по индексу, выходящему за его пределы.

Что использовать в большинстве случаев:

Для ожидаемых ошибок (например, "файл не найден", ошибка валидации ввода, недоступность сетевого ресурса) всегда следует использовать возврат значения типа error. Это идиоматический и основной способ обработки ошибок в Go.

Пример:

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

    fmt.Println("Вызываем функцию, которая может запаниковать.")
    mightPanic(true)
    fmt.Println("Эта строка не будет выполнена, если была паника.")
}

func mightPanic(shouldPanic bool) {
    if shouldPanic {
        panic("произошла критическая ошибка")
    }
}