Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Как правильно использовать 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
-
Всегда проверяйте возвращаемое значение:
if r := recover(); r != nil { // Только если было восстановление } -
Не злоупотребляйте recover():
- Используйте для обработки непредвиденных ситуаций
- Не заменяйте обычную обработку ошибок через
error - Идеальные случаи: сторонние библиотеки, критичные участки
-
Особенности работы с горутинами:
func main() { go func() { defer func() { if r := recover(); r != nil { fmt.Println("Паника в горутине перехвачена") } }() panic("в горутине") }() time.Sleep(time.Second) }Каждая горутина должна иметь свой собственный
recover(). -
Сочетание с defer: Порядок выполнения имеет значение:
func example() { defer fmt.Println("Это выполнится первым") defer func() { recover() // Это выполнится вторым }() defer fmt.Println("Это выполнится последним перед паникой") panic("стоп") } -
Восстановление и повторная паника: Иногда нужно логировать и снова вызывать панику:
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.