Почему 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:

Этот выпуск содержит три изменения языка:

  1. Функции 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 - очень параллельный язык в природе и основной синтаксис. Сценарий, когда некоторые вычисления, локализованные для параллельного отказа процесса, но дырочная система продолжает работать, можно рассматривать как нормальную. На мой взгляд, паника и восстановление связаны с обработкой ошибок, а не с исключениями.