В чем разница между panic и error в Go и как обрабатывать panic?

Ответ

В Go существует четкое разделение между ожидаемыми ошибками (error) и критическими сбоями (panic).

Error

  • Это ожидаемое, предсказуемое состояние ошибки. Например, файл не найден, сетевое соединение прервано, неверный ввод пользователя.
  • В Go идиоматично возвращать ошибки как последнее значение из функции: val, err := someFunc().
  • Обработка error — это часть нормального потока выполнения программы. Вы должны проверять if err != nil и принимать решение, что делать дальше.

Panic

  • Это непредвиденная, критическая ошибка времени выполнения, которая делает дальнейшее выполнение программы невозможным или бессмысленным. Например, обращение по nil-указателю, выход за пределы массива.
  • panic останавливает выполнение текущей функции, запускает выполнение всех отложенных вызовов (defer) в обратном порядке, а затем "всплывает" вверх по стеку вызовов, проделывая то же самое в вызывающих функциях. Если panic не будет обработана, программа аварийно завершится.

Обработка panic с помощью recover

panic можно перехватить и "обезвредить" с помощью встроенной функции recover. Это позволяет программе продолжить выполнение.

Правила использования recover:

  1. recover работает только внутри отложенной функции (defer). Вызов recover в любом другом месте вернет nil и не будет иметь эффекта.
  2. Она перехватывает значение, переданное в panic, и позволяет его проанализировать.

Пример:

package main

import "fmt"

func safeDivision(a, b int) int {
    // defer выполняется перед выходом из функции, в том числе при панике
    defer func() {
        // recover() перехватывает панику
        if r := recover(); r != nil {
            fmt.Printf("Recovered from panic: %vn", r)
            // Здесь можно логировать ошибку или выполнять очистку
        }
    }()

    fmt.Println("Calculating...")
    // Эта строка вызовет панику, если b равно 0
    result := a / b
    return result
}

func main() {
    safeDivision(10, 0)
    fmt.Println("Program continues to run after panic was recovered.")
}

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