Теперь вы точно знаете, что во время выполнения программы могут возникать ошибки.
-
Иногда ошибки не несут большой проблемы — к примеру, пользователь передал неправильные данные. В таком случае достаточно сделать запись в логах.
-
Иногда ошибки могут создать проблемы, когда не удаётся получить информацию от внешних ресурсов. В таком случае можно уведомить пользователя или попробовать получить информацию ещё раз.
-
Иногда ошибки могут быть такими, что дальнейшее продолжение программы нецелесообразно и даже опасно. Например, не удалось подключиться к базе данных, возникли ошибки в конфигурационных файлах.
Последние ситуации считаются аварийными и требуют немедленного завершения работы программы. Это делается с помощью механизма паники. С ним вы уже знакомы: программа вызывает панику, например, при разыменовании
nil
-указателя или неуспешном приведении типа. В этой главе рассмотрим панику детальнее.
В аварийной ситуации программа останавливает работу, вызываются отложенные функции defer
и выводится сообщение об ошибке. В этом сообщении, кроме текста, указывается состояние стека вызовов функций.
Чтобы создать аварийную ситуацию, нужно вызвать встроенную функцию panic
. В аргументе при вызове panic
можно передать значение любого типа:
Если запустить этот код, программа завершит работу с такой ошибкой и стеком:
Старт
panic: open test.txt: no such file or directory
goroutine 1 [running]:
main.myfunc(...)
/tmp/sandbox910916036/prog.go:10
main.main()
/tmp/sandbox910916036/prog.go:16 +0x11e
Программа не дошла до печати строки "Финиш"
. Сразу можно увидеть не только причину ошибки, но и номер строки, где эта ошибка произошла.
Кроме функции panic
, аварийные ситуации может генерировать среда выполнения Go по таким сценариям, как деление на ноль, разыменование нулевого указателя, обращение по индексу за границами массива или слайса. Подобные ошибки могут возникнуть в любой функции. Например, нерационально проверять выход за границы массива или равенство указателя nil
при каждой операции:
Если запустить этот фрагмент, программа завершит работу с таким текстом ошибки:
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
main.mypanic()
/tmp/sandbox852111250/prog.go:7 +0x18
main.main()
/tmp/sandbox852111250/prog.go:11 +0x25
Паника возникла в строке номер 7, в функции, которая была вызвана в строке 11.
Note
Не рекомендуется использовать функцию
panic
для обработки всех ошибок. Стоит избегатьpanic
в библиотеках. Если такая функция всё-таки необходима, нужно обязательно это задокументировать.
pаnic
— ресурсозатратный механизм, который не всегда можно остановить. Именно поэтому паника не рекомендуется к применению.
Вот пример описания для функции MustCompile
стандартного пакета regexp
:
Создание аварийной ситуации возможно, когда непонятно, как программа будет выполняться дальше и лучше завершить процесс сейчас, чем получить более серьёзные проблемы. Например, паника уместна в тестах и сюжетах, когда программа на старте не может прочитать файл конфигурации. Ещё panic
часто встречается в конструкциях switch — case
, если значение переменной не соответствует ни одному варианту.
Рекомендуется логировать каждый случай паники и подключать систему оповещений разработчиков для быстрого информирования о проблеме.
Чем аварийная ситуация отличается от штатного завершения программы? В Go есть ещё одна функция — recover
, которая позволяет восстановить выполнение программы в случае паники. Если на момент вызова recover
произошла аварийная ситуация, то recover
завершает её и возвращает значение ошибки (аргумент при вызове panic
). Если аварийной ситуации не было, recover
ничего не делает и возвращает nil
.
Note
Не передавайте
nil
при вызовеpanic
— еслиrecover
вернёт его, вы не поймёте, что была аварийная ситуация.
Где вызвать функцию recover
, если программа остановила своё выполнение? Так как при возникновении аварийной ситуации вызываются функции defer
, вставим вызов recover
туда.
Напомним, что defer
-вызовы будут вызваны в любом случае, даже если произошла паника. Мы рассматривали это подробно в разделе, посвящённом defer
:
// Cтарт
// Возникла паника: aварийная ситуация
// Финиш
Связка функций panic
и recover
напоминает механизм исключений try — catch
в других языках. Но если стандартная работа с ошибками не требует дополнительных ресурсов, то вызов panic
приводит к раскручиванию стека, а это затратная операция.
Несмотря на то что функция recover
позволяет продолжить выполнение программы, её следует применять лишь в оправданных ситуациях. Например, стандартный веб-сервер использует recover
, чтобы паника в одном обработчике не завершила весь процесс.
Также стоит помнить о возможных утечках памяти и неопределённых значений глобальных переменных после выхода из аварийной ситуации, что может привести к новым конфликтам. Существуют ситуации, когда паника не может быть отловлена, и программа однозначно завершается. Это, например, ситуации, связанные с конкурентностью или тем, что на компьютере закончилась память.
Ключевые мысли
- Паника прерывает нормальное исполнение программы, поэтому применима только в аварийных ситуациях.
- Паника вызывает непредвиденные расходы по ресурсам, связанные с раскруткой стека.
- Отложенные вызовы
defer
вызываются всегда. Именно там стоит ловить панику функциейrecover
. - Не всякую панику можно восстановить.
- Даже если в пакете приходится вызывать панику, то её не следует выпускать за пределы пакета.
📂 Go | Последнее изменение: 30.08.2024 20:37