Что такое panic в Go и как с ним работать
Что проверяют
Понимаете ли вы, что паника не замена ошибкам, и знаете ли ограничения recover.
Как отвечать
Паника это аварийное прекращение обычного хода выполнения. Она разматывает стек, выполняя по пути отложенные вызовы, и, если её никто не перехватил, завершает программу с выводом стека.
Возникает двумя путями: явным вызовом panic() и от рантайма. Рантайм паникует при разыменовании нулевого указателя, выходе за границы среза, делении на ноль, записи в неинициализированную карту, неудачном приведении типа без второго значения, закрытии закрытого канала.
Перехват делается функцией recover, и у неё жёсткие ограничения:
- работает только внутри отложенного вызова. Вызов
recoverв обычном коде вернётnil; - действует только в своей горутине. Паника в отдельной горутине не будет перехвачена в родительской и уронит весь процесс. Это самая частая причина падений в проде;
defer func() {
if r := recover(); r != nil {
log.Printf("восстановление после паники: %v", r)
}
}()Главное правило по применению: паника не для обычных ошибок. Ошибки в Go возвращаются значением, и вызывающий решает, что с ними делать. Паника уместна в двух случаях: невосстановимая ошибка при инициализации (отсюда семейство функций с приставкой Must, вроде regexp.MustCompile) и нарушение инварианта, означающее ошибку программиста.
На границе процесса паника всё же перехватывается, чтобы не терять сервис из-за одного запроса: net/http делает это сам, но соединение рвёт, поэтому обычно пишут своё промежуточное звено, которое логирует стек и отдаёт код 500. Перехват без логирования недопустим: молча проглоченная паника оставляет систему в неизвестном состоянии.
Где спотыкаются
Используют панику вместо возврата ошибки.
Ставят recover вне отложенного вызова.
Рассчитывают перехватить панику из соседней горутины.
Проглатывают панику без записи в журнал.