Почему `recover()` следует использовать именно в отложенной функции (`defer`) и какова его роль?

Ответ

Основная задача recover() — перехватить панику, чтобы предотвратить аварийное завершение программы и выполнить код для graceful shutdown или обработки ошибки.

recover() эффективно работает только при вызове внутри отложенной функции (defer), потому что:

  1. Когда возникает паника, нормальное выполнение функции немедленно прекращается.
  2. Исполнение переходит к выполнению defer функций, которые были отложены в этой горутине.
  3. Только на этом этапе recover() может "поймать" панику. Если вызвать recover() вне defer, он просто вернет nil, так как в момент его вызова паники еще (или уже) нет.

Пример корректного использования:

func safeFunc() {
    defer func() {
        // recover() перехватывает значение, переданное в panic()
        if r := recover(); r != nil {
            fmt.Println("Перехвачена паника:", r)
        }
    }()

    fmt.Println("Начало выполнения safeFunc")
    // Симуляция критической ошибки
    panic("что-то пошло не так")
    // Этот код никогда не выполнится
    fmt.Println("Завершение выполнения safeFunc")
}

Ключевые моменты:

  • Место вызова: recover() должен быть вызван непосредственно из defer функции. Вызов из функции, которую вызывает defer, не сработает.
  • Возвращаемое значение: Возвращает значение, переданное в panic(), или nil, если паники не было.
  • Продолжение выполнения: После успешного recover() паника прекращается, и выполнение продолжается со следующего оператора после вызова функции, в которой произошла паника. Сама запаниковавшая функция не возобновляет свою работу.

Ответ 18+ 🔞

А, ну ты смотри, про recover() спрашиваешь! Ну, это, блядь, как такой спасательный круг в океане из говна и палок, который только в одном месте работает.

Основная его фишка — поймать панику, чтобы твоя программа не вылетела к ебене матери с криком "ааа, всё пропало!", а тихонечко, по-взрослому, подтерлась и сделала вид, что так и надо.

Но вот хуйня в чём: recover() работает только внутри отложенной функции, то есть в defer. А почему? Да потому что логика, блядь, железная:

  1. Паника случилась — всё, пиздец, функция резко замирает, как мудак, который только что понял, что нажал не на ту кнопку.
  2. Управление передаётся не куда-нибудь, а прямиком в отложенные defer'ы, которые эта функция накопила, как долги.
  3. И вот только тут, в этом священном месте, recover() может поднять свою ебальник и сказать: "Стоять! Паника? А ну-ка иди сюда, сука, я тебя съем!".

Вызови его где-нибудь в другом месте — он тебе вернёт nil и посмотрит на тебя, как на идиота, потому что паники в этот момент либо ещё нет, либо уже всё.

Вот, смотри, как правильно, ёпта:

func safeFunc() {
    defer func() {
        // recover() тут как раз и ловит то, чем тебя в panic() швырнули
        if r := recover(); r != nil {
            fmt.Println("Перехвачена паника:", r)
        }
    }()

    fmt.Println("Начало выполнения safeFunc")
    // Имитируем, что всё пошло по пизде
    panic("что-то пошло не так")
    // Сюда мы, ясное дело, уже никогда не попадём
    fmt.Println("Завершение выполнения safeFunc")
}

Важные нюансы, блядь, чтобы не обосраться:

  • Где вызывать: recover() должен сидеть прямо внутри defer функции. Если ты вызовешь его из какой-то другой функции, которую вызвал внутри defer — нихуя не получится, паника проскочит мимо, как похуист мимо проблем.
  • Что вернёт: Вернёт ровно то, чем ты в panic() кинулся. Если никто не кидался — вернёт nil, и жизнь прекрасна.
  • Что дальше: Если recover() сработал, паника считается усмирённой. Дальше программа пойдёт выполнять код, который идёт после вызова той самой запаниковавшей функции. Сама же эта функция — ей пиздец, она уже не оправится, её выполнение на том и закончилось.