← Назад к вопросам

Как правильно объявить Recover?

2.0 Middle🔥 162 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как правильно использовать recover() в Go

Основные принципы объявления и использования

В Go recover() — это встроенная функция, которая позволяет перехватывать панику (panic) и восстанавливать нормальное выполнение программы. Важно понимать, что recover() работает ТОЛЬКО внутри отложенных функций (deferred functions).

Правильный синтаксис и расположение

Неправильно вызывать recover() прямо в теле функции:

func wrongExample() {
    result := recover() // НЕ СРАБОТАЕТ вне отложенной функции
    fmt.Println(result)
}

Правильное объявление — всегда внутри defer:

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

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

1. Базовый паттерн восстановления

func safeFunction() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Паника перехвачена: %v", r)
            // Можно выполнить очистку ресурсов
        }
    }()
    
    dangerousOperation()
}

2. Восстановление с возвратом ошибки

func safeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("операция вызвала панику: %v", r)
        }
    }()
    
    potentiallyPanickingCode()
    return nil
}

3. Селективное восстановление (по типам)

func handlePanic() {
    defer func() {
        if r := recover(); r != nil {
            switch v := r.(type) {
            case runtime.Error:
                log.Println("Ошибка времени выполнения:", v)
            case string:
                log.Println("Строковая паника:", v)
            case error:
                log.Println("Ошибка:", v)
            default:
                log.Println("Неизвестная паника:", v)
            }
        }
    }()
}

Критические правила и best practices

  1. Всегда проверяйте возвращаемое значение:

    if r := recover(); r != nil {
        // Только если было восстановление
    }
    
  2. Не злоупотребляйте recover():

    • Используйте для обработки непредвиденных ситуаций
    • Не заменяйте обычную обработку ошибок через error
    • Идеальные случаи: сторонние библиотеки, критичные участки
  3. Особенности работы с горутинами:

    func main() {
        go func() {
            defer func() {
                if r := recover(); r != nil {
                    fmt.Println("Паника в горутине перехвачена")
                }
            }()
            panic("в горутине")
        }()
        
        time.Sleep(time.Second)
    }
    

    Каждая горутина должна иметь свой собственный recover().

  4. Сочетание с defer: Порядок выполнения имеет значение:

    func example() {
        defer fmt.Println("Это выполнится первым")
        defer func() {
            recover() // Это выполнится вторым
        }()
        defer fmt.Println("Это выполнится последним перед паникой")
        panic("стоп")
    }
    
  5. Восстановление и повторная паника: Иногда нужно логировать и снова вызывать панику:

    defer func() {
        if r := recover(); r != nil {
            logError(r)
            panic(r) // Повторно вызываем для верхнеуровневой обработки
        }
    }()
    

Распространенные антипаттерны

// ❌ НЕ ДЕЛАЙТЕ ТАК:
func antiPattern1() {
    recover() // Бесполезно вне defer
}

func antiPattern2() {
    defer recover() // Не сработает, recover нужно вызывать внутри функции
}

func antiPattern3() {
    defer func() {
        recover() // Вызывается, но результат игнорируется
        // Паника "проглотится" без логирования
    }()
    panic("скрытая ошибка")
}

Практический пример из production

func ProcessData(data []byte) (result Result, err error) {
    // Отслеживаем утечки ресурсов
    resource := AcquireResource()
    defer func() {
        resource.Release()
        
        // Восстанавливаемся после возможной паники при освобождении
        if r := recover(); r != nil {
            err = fmt.Errorf("ошибка при освобождении ресурсов: %v", r)
        }
    }()
    
    // Основная обработка с защитой от паники
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("обработка данных вызвала панику: %v", r)
        }
    }()
    
    result = parseAndValidate(data)
    return result, nil
}

Выводы

Правильное использование recover() требует:

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

recover() — это инструмент для обработки исключительных ситуаций, а не механизм обычного контроля потока выполнения. Используйте его там, где возможны непредвиденные паники, которые не должны завершать всю программу, но не заменяйте им стандартную обработку ошибок через возвращаемые значения error.