Объясните механизм `panic`, `defer` и `recover` в Go. В каких случаях их следует использовать?

Ответ

Это механизм Go для обработки исключительных, непредвиденных ситуаций, которые делают дальнейшее выполнение программы невозможным в текущем контексте.

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

  • defer — это ключевое слово, которое откладывает выполнение вызова функции до тех пор, пока содержащая её функция не завершит свое выполнение (либо через return, либо из-за panic). Отложенные вызовы выполняются в порядке LIFO (Last-In, First-Out) — последняя отложенная функция выполнится первой.

  • recover — это встроенная функция, которая позволяет перехватить управление горутиной, находящейся в состоянии паники. recover полезна только внутри defer-функций. Если текущая горутина не паникует, вызов recover вернет nil. Если же горутина паникует, вызов recover перехватит значение, переданное в panic, и восстановит нормальное выполнение.

Пример:

func main() {
    fmt.Println("Start")
    safeDivide(10, 0)
    fmt.Println("End")
}

func safeDivide(a, b int) {
    defer func() {
        // recover работает только внутри defer
        if r := recover(); r != nil {
            fmt.Printf("Перехвачена паника: %vn", r)
        }
    }()

    result := a / b // Эта строка вызовет панику
    fmt.Printf("Результат: %dn", result) // Эта строка не выполнится
}

Вывод:

Start
Перехвачена паника: runtime error: integer divide by zero
End

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

Основное правило — не использовать panic для обычной обработки ошибок. Для этого в Go есть явный возврат error.

Допустимые случаи использования panic/recover:

  1. Предотвращение падения всего сервера. Например, в веб-сервере одна горутина, обрабатывающая HTTP-запрос, может запаниковать из-за непредвиденной ошибки. С помощью recover в defer можно перехватить эту панику, записать лог, отдать клиенту ошибку 500, но при этом сам сервер продолжит работать и обслуживать другие запросы.
  2. Ошибки в программе, которые не должны происходить. Например, если программа достигает состояния, которое считается невозможным (например, case в switch, который никогда не должен сработать). Это сигнал о серьезной ошибке в логике программиста.