Почему Go добавляет панику и восстанавливается в дополнение к обработке ошибок?
Почему Go в конечном итоге принял обработку исключений с помощью panic/recover, когда язык настолько идиоматичен и является сильным сторонником кодов ошибок? В каких сценариях дизайнеры Go envision не обрабатываются кодами ошибок и требуют паники/восстановления?
Я понимаю, что соглашение говорит limit panic/recover, но среда выполнения также ограничивает их тем, что они не могут использоваться как общий throw/catch в С++?
Ответы
Ответ 1
Некоторая история:
В первые дни Go (до версии 1.0) не было recover()
. Вызов panic()
приведет к прекращению действия приложения без каких-либо ограничений.
Я нашел исходное обсуждение, которое привело к добавлению recover()
, вы можете прочитать его на форуме обсуждения golang-орехов:
Предложение для механизма, подобного исключению
Остерегайтесь: обсуждение датируется 25 марта 2010 года, и оно довольно утомительно и долго (150 сообщений на 6 страниц).
В конце концов он был добавлен в 2010-03-30:
Этот выпуск содержит три изменения языка:
- Функции
panic
и recover
, предназначенные для сообщения и восстановления неудача, были добавлены в спецификацию:
http://golang.org/doc/go_spec.html#Handling_panics
В связанном изменении panicln
исчезает, а panic
теперь является единственным аргументом функция. Паника и восстановление распознаются компиляторами gc, но новые поведение еще не реализовано.
Многозадачные значения и соглашения обеспечивают более чистый способ обработки ошибок в Go.
Это не означает, однако, что в некоторых (редких) случаях восстановление паники не полезно.
Цитата из официального FAQ: Почему у Go нет исключений?
Go также имеет несколько встроенных функций для сигнализации и восстановления из действительно исключительных условий. Механизм восстановления выполняется только как часть состояния функции, которое сбрасывается после ошибки, что является достаточным для обработки катастрофы, но не требует дополнительных структур управления и при правильном использовании может привести к очистке кода обработки ошибок.
Вот пример "реальной жизни", когда/как это может быть полезно: цитирование из сообщения в блоге "Отложить, панику и восстановление" :
Для реального примера паники и восстановления см. json package из стандартной библиотеки Go. Он декодирует JSON-кодированные данные с набором рекурсивных функций. Когда встречается некорректный JSON, парсер вызывает панику, чтобы развернуть стек до вызова функции верхнего уровня, который восстанавливается из паники и возвращает соответствующее значение ошибки (см. Методы "error" и "unmarshal" типа decodeState в decode.go).
Другой пример - это когда вы пишете код (например, пакет), который вызывает функцию, предоставленную пользователем. Вы не можете доверять предоставленной функции, чтобы она не паниковала. Один из способов - не иметь дело с этим (пусть паника заканчивается), или вы можете выбрать "защитить" свой код, восстановив при этом панику. Хорошим примером этого является обработчики или функции обработчика), и если ваши обработчики будут паниковать, сервер будет оправиться от этой паники и не позволить вашему полному приложению умереть.
Как вы должны их использовать:
Соглашение в библиотеках Go заключается в том, что даже когда пакет использует панику внутри, его внешний API все еще содержит явные значения возврата ошибки.
Связанные и полезные показания:
http://blog.golang.org/defer-panic-and-recover
http://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right
https://golang.org/doc/faq#exceptions
http://evanfarrer.blogspot.co.uk/2012/05/go-programming-language-has-exceptions.html
Ответ 2
Я думаю, что ваш вопрос является результатом умственной модели, которую вы поддерживаете, которая внедряется популярными основными языками, такими как Java, С++, С#, PHP и zillions других, которые просто получили исключения.
Дело в том, что исключения сами по себе не являются неправильной концепцией, но злоупотребляют ими для обработки случаев, которые на самом деле не являются исключительными. Мой личный питомец - это API-интерфейс обработки файловой системы Java (и .NET, который скопировал версию Java почти дословно): почему на Земле отказ открыть файл приводит к исключению, если этот файл не существует? Файловая система является неотъемлемо яркой средой, и она указана как яркая, поэтому единственный способ убедиться, что файл существует, прежде чем открыть его для чтения, - это просто открыть его, а затем проверить, нет ли ошибки "файл не существует": случай файл, не существующий, не является исключительным вообще.
Следовательно, Go ясно отделяет исключительные случаи от простых нормальных ошибок.
Девиз позиции "Go" при обработке ошибок "Ошибки - это значения", и поэтому нормальные ожидаемые ошибки обрабатываются как значения, а panic()
служит для обработки исключительных случаев. Хороший простой пример:
-
Попытка разыменовать указатель nil
приводит к panic
.
Обоснование: ваш код продолжался и попытался разыменовать указатель, который не указывает на какое-либо значение. В приведенном ниже коде очевидно, что это значение будет доступно в результате операции разыменования. Следовательно, поток управления явно не может нормально идти каким-либо разумным образом, и почему это исключительная ситуация: в правильной программе разыменование указателей nil
не может произойти.
-
Удаленный конец потока TCP резко закрыл свою сторону потока, а следующая попытка прочитать из него привела к ошибке.
Это довольно нормальная ситуация: нельзя с уверенностью ожидать, что сеанс TCP будет устойчивым: перебои в сети, падения пакетов, неожиданные отключения электроэнергии происходят, и мы должны быть готовы к неожиданным закрытию потока нашими удаленными одноранговыми узлами.
Небольшой поворот до panic()
заключается в том, что Go не заставляет вас слепо следовать определенным догматам, и вы можете свободно "злоупотреблять" паникой/восстанавливаться в жестко контролируемых конкретных случаях, таких как вырваться из глубоко вложенного цикла обработки королем panic
с значением ошибки определенного типа, известным и проверенным на сайте, выполняющим recover
.
Дальнейшее чтение:
Ответ 3
Я думаю, что причина - модель concurrency. Go - очень параллельный язык в природе и основной синтаксис. Сценарий, когда некоторые вычисления, локализованные для параллельного отказа процесса, но дырочная система продолжает работать, можно рассматривать как нормальную. На мой взгляд, паника и восстановление связаны с обработкой ошибок, а не с исключениями.