В каких случаях и как правильно обрабатывать панику (panic) в Go?

Ответ

В Go panic предназначена для сигнализации о невосстановимых ошибках — таких, которые не должны были произойти в нормальных условиях (например, выход за пределы массива, разыменование nil-указателя). Это не механизм для обработки обычных ошибок (как try-catch в других языках).

Панику следует перехватывать с помощью recover() только в строго определенных местах.

Когда и где использовать recover

recover() всегда используется внутри defer-функции. Если panic не было, recover() вернет nil.

func safeRun() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Перехвачена паника: %vn%s", r, debug.Stack())
        }
    }()

    // Код, который потенциально может запаниковать
    mightPanic()
}

Основные сценарии для recover:

  1. На верхнем уровне приложения: В функции main или в обработчике HTTP-запросов, чтобы паника в одной горутине (например, при обработке одного запроса) не привела к падению всего сервера. Цель — залогировать ошибку и продолжить работу.
  2. В "долгоживущих" горутинах: В воркерах или фоновых задачах, чтобы случайная паника не остановила важный процесс.
  3. При работе с CGo или сторонними библиотеками: В качестве защитной меры, если есть подозрение, что внешний код может вызвать панику.

Что НЕ следует делать

  • Не используйте panic/recover для контроля потока выполнения. Для ожидаемых ошибок (файл не найден, неверный ввод пользователя) всегда возвращайте значение типа error.
  • Библиотечный код не должен паниковать. Хорошая библиотека всегда возвращает ошибки своим пользователям, а не "взрывается". Паника в библиотеке — признак бага.