Ответ
Это механизм 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:
- Предотвращение падения всего сервера. Например, в веб-сервере одна горутина, обрабатывающая HTTP-запрос, может запаниковать из-за непредвиденной ошибки. С помощью
recoverвdeferможно перехватить эту панику, записать лог, отдать клиенту ошибку 500, но при этом сам сервер продолжит работать и обслуживать другие запросы. - Ошибки в программе, которые не должны происходить. Например, если программа достигает состояния, которое считается невозможным (например,
caseвswitch, который никогда не должен сработать). Это сигнал о серьезной ошибке в логике программиста.
Ответ 18+ 🔞
А, слушай, смотри, вот эта штука в Go — panic, defer, recover — это, блядь, как аварийный люк в подводной лодке, только для кода. Когда всё пошло по пизде и обычным путём уже не выкрутиться.
Представь: твоя программа плывёт себе спокойно, и тут panic — это как крик "ЁБАНА-А-АТЬ!" внутри горутины. Всё, стоп, приехали. Выполнение резко обрывается, и начинается, сука, цепная реакция: функция, которая запаниковала, начинает сворачиваться, но перед этим выполняет все отложенные дела — те самые defer-вызовы. Потом эта паника, как волна дерьма, идёт наверх по стеку, ко всем, кто её вызывал, и так до тех пор, пока вся программа не накроется медным тазом с аварийным логом.
А defer — это типа "сделай потом, чувак". Захотел закрыть файл, освободить мьютекс или просто выпить пива после работы — вешаешь defer, и эта хуйня выполнится гарантированно, когда функция будет заканчиваться. Неважно, как — штатно через return или потому что её, блядь, паникой накрыло. И что самое важное — выполнятся они в обратном порядке, как тарелки у циркача: последний defer — первый на выходе.
И вот тут появляется наш спасатель — recover. Это как сетка под трапецией. Работает он ТОЛЬКО внутри этих самых defer-функций. Если горутина спокойна, recover вернёт nil, типа "всё чисто, расслабься". Но если она в панике, то recover перехватит то самое значение, которое крикнули в panic, и, внимание, ВОССТАНОВИТ нормальную работу. Паника остановится, и можно будет жить дальше.
Вот, смотри, наглядный пиздец:
func main() {
fmt.Println("Начало, всё ок")
safeDivide(10, 0) // Сейчас начнётся...
fmt.Println("Конец, мы живы!") // И эта строка ВЫПОЛНИТСЯ!
}
func safeDivide(a, b int) {
defer func() {
// recover работает ТОЛЬКО тут, внутри defer!
if r := recover(); r != nil {
fmt.Printf("Перехватили панику, сука: %vn", r) // Ловим крик "деление на ноль!"
}
}()
result := a / b // На этой строке — ПИЗДЕЦ, runtime error: integer divide by zero
fmt.Printf("Результат: %dn", result) // Сюда мы уже никогда не попадём
}
Что на экране:
Начало, всё ок
Перехватили панику, сука: runtime error: integer divide by zero
Конец, мы живы!
Главное правило, блядь, выучи как "Отче наш":
НЕ ИСПОЛЬЗУЙ panic ДЛЯ ОБЫЧНЫХ ОШИБОК! Для этого есть error, возвращай его явно и обрабатывай. Это основа идиомы Go.
А panic/recover — это для совсем уж крайних, ебаных случаев:
- Чтобы сервер не лег. Допустим, в горутине, обрабатывающей HTTP-запрос, случилось что-то невообразимое. Вместо того чтобы падать всему процессу, мы в
deferна уровне хендлера ставимrecover, логируем жуткую ошибку, клиенту отдаём 500 Internal Server Error, а сервер — охуенно! — продолжает работать и обслуживать других. - Ошибки, которых в принципе быть не должно. Типа
default:вswitch, который никогда не должен сработать, или получение элемента из мапы, которая гарантированно должна его содержать. Это крик "Я, блядь, как программист, облажался, тут чушь какая-то!" — вот для такогоpanicв самый раз.