Язык Google Go не имеет исключений как выбор дизайна, и Линус, известный в Linux, назвал исключения чушью. Почему?
Язык Google Go не имеет исключений как выбор дизайна, и Линус, известный в Linux, назвал исключения чушью. Почему?
Ответы:
Исключения упрощают написание кода, в котором генерируемое исключение нарушает инварианты и оставляет объекты в несогласованном состоянии. По сути, они заставляют вас помнить, что почти каждое сделанное вами утверждение потенциально может выбросить, и обрабатывать это правильно. Это может быть сложно и нелогично.
Рассмотрим на простом примере что-то вроде этого:
class Frobber
{
int m_NumberOfFrobs;
FrobManager m_FrobManager;
public:
void Frob()
{
m_NumberOfFrobs++;
m_FrobManager.HandleFrob(new FrobObject());
}
};
Если предположить , что FrobManager
воля delete
к FrobObject
, это выглядит нормально, не так ли? Или, может быть, нет ... Представьте, что либо либо, FrobManager::HandleFrob()
либо operator new
выдаст исключение. В этом примере приращение m_NumberOfFrobs
не откатывается. Таким образом, любой, кто использует этот экземпляр, Frobber
может иметь поврежденный объект.
Этот пример может показаться глупым (ладно, мне пришлось немного растянуться, чтобы построить его :-)), но вывод состоит в том, что если программист не думает постоянно об исключениях и не проверяет каждую перестановку состояния назад всякий раз, когда есть броски, вы попадаете в беду.
Например, вы можете думать об этом, как о мьютексах. Внутри критического раздела вы полагаетесь на несколько операторов, чтобы убедиться, что структуры данных не повреждены и другие потоки не могут видеть ваши промежуточные значения. Если какое-либо из этих утверждений случайно не выполняется, вы попадаете в мир боли. Теперь уберите блокировки и параллелизм и подумайте о каждом подобном методе. Думайте о каждом методе как о транзакции перестановок состояния объекта, если хотите. В начале вызова метода объект должен быть в чистом состоянии, а в конце также должно быть чистое состояние. Между ними переменная foo
может не соответствоватьbar
, но ваш код со временем исправит это. Что означают исключения, так это то, что любое из ваших утверждений может прервать вас в любой момент. В каждом отдельном методе ответственность лежит на вас, чтобы исправить это и откатиться, когда это произойдет, или упорядочить свои операции, чтобы выбросы не влияли на состояние объекта. Если вы ошиблись (а сделать такую ошибку легко), то вызывающий абонент увидит ваши промежуточные значения.
Такие методы, как RAII, которые программисты на C ++ любят упоминать как окончательное решение этой проблемы, имеют большое значение для защиты от этого. Но они не серебряная пуля. Это гарантирует, что вы высвобождаете ресурсы сразу же, но не избавляет вас от необходимости думать о повреждении состояния объекта и о том, что вызывающие абоненты видят промежуточные значения. Таким образом, для многих людей проще сказать, исходя из стиля кодирования, без исключений . Если вы ограничиваете тип кода, который пишете, эти ошибки труднее вносить. Если вы этого не сделаете, довольно легко ошибиться.
О безопасном кодировании на C ++ написаны целые книги. Многие эксперты ошиблись. Если это действительно так сложно и имеет так много нюансов, возможно, это хороший знак, что вам нужно игнорировать эту функцию. :-)
Причина отсутствия исключений в Go объясняется в FAQ по языковому дизайну Go:
Исключения - похожая история. Было предложено несколько вариантов исключений, но каждый из них значительно усложняет язык и время выполнения. По самой своей природе исключения охватывают функции и, возможно, даже горутины; они имеют самые разные последствия. Есть также опасения по поводу того, какое влияние они окажут на библиотеки. Они, по определению, являются исключительными, но опыт работы с другими поддерживающими их языками показывает, что они оказывают глубокое влияние на спецификации библиотек и интерфейсов. Было бы неплохо найти дизайн, который позволил бы им быть действительно исключительными, не поощряя превращение общих ошибок в особый поток управления, который требует компенсации от каждого программиста.
Как и дженерики, исключения остаются открытой проблемой.
Другими словами, они еще не выяснили, как поддерживать исключения в Go таким образом, который они считают удовлетворительным. Они не говорят, что исключения плохи сами по себе ;
ОБНОВЛЕНИЕ - май 2012 г.
Теперь дизайнеры Go слезли с забора. В их FAQ теперь говорится следующее:
Мы считаем, что привязка исключений к структуре управления, как в идиоме try-catch-finally, приводит к запутанному коду. Это также побуждает программистов помечать слишком много обычных ошибок, таких как невозможность открытия файла, как исключительные.
Go использует другой подход. Для простой обработки ошибок многозначные возвращаемые значения Go позволяют легко сообщить об ошибке, не перегружая возвращаемое значение. Канонический тип ошибки в сочетании с другими функциями Go делает обработку ошибок приятной, но сильно отличается от таковой на других языках.
Go также имеет несколько встроенных функций для сигнализации и восстановления после действительно исключительных условий. Механизм восстановления выполняется только как часть состояния функции, которая прерывается после ошибки, что достаточно для обработки катастрофы, но не требует дополнительных структур управления и, при правильном использовании, может привести к созданию чистого кода обработки ошибок.
Подробнее см. В статье «Отложить, паника и восстановление».
Итак, краткий ответ заключается в том, что они могут делать это по-другому, используя многозначный возврат. (И в любом случае у них есть форма обработки исключений.)
... и Линус, известный в Linux, назвал исключения чушью.
Если вы хотите знать, почему Линус считает исключения - это чушь, лучше всего поискать его работы по этой теме. Единственное, что я отследил до сих пор, - это цитата, встроенная в пару писем на C ++ :
«Вся обработка исключений в C ++ фундаментально нарушена. Особенно это касается ядер».
Вы заметите, что он говорит об исключениях C ++ в частности, а не об исключениях в целом. (И исключения C ++ , по- видимому, имеют некоторые проблемы, из-за которых их сложно использовать правильно.)
Я пришел к выводу, что Линус вообще не называл исключения (в общем) «чушью»!
Исключения сами по себе неплохи, но если вы знаете, что они будут происходить часто, они могут быть дорогими с точки зрения производительности.
Эмпирическое правило состоит в том, что исключения должны отмечать исключительные условия, и вы не должны использовать их для управления ходом выполнения программы.
Я не согласен с тем, что «исключать исключения можно только в исключительной ситуации». Хотя в целом это правда, это заблуждение. Исключения составляют условия ошибки (сбои выполнения).
Независимо от того, какой язык вы используете, возьмите копию Руководства по дизайну Framework : соглашения, идиомы и шаблоны для многоразовых библиотек .NET (2-е издание). Глава о генерировании исключений не имеет аналогов. Несколько цитат из первого издания (у меня 2-е):
Есть страницы с заметками о преимуществах исключений (согласованность API, выбор местоположения кода обработки ошибок, повышенная надежность и т. Д.). Есть раздел о производительности, который включает в себя несколько шаблонов (Tester-Doer, Try-Parse).
Исключения и обработка исключений - это неплохо. Как и любую другую функцию, ими можно злоупотреблять.
С точки зрения golang, я полагаю, что отсутствие обработки исключений делает процесс компиляции простым и безопасным.
С точки зрения Линуса, я понимаю, что код ядра ВСЕ касается угловых случаев. Так что есть смысл отказаться от исключений.
Исключения имеют смысл в коде, если можно оставить текущую задачу на полу, и где общий код случая имеет большее значение, чем обработка ошибок. Но они требуют генерации кода от компилятора.
Например, они подходят для большинства высокоуровневых кодов, ориентированных на пользователя, таких как код веб-приложений и приложений для настольных компьютеров.
Исключения сами по себе не являются «плохими», это способ обработки исключений иногда бывает плохим. Есть несколько рекомендаций, которые можно применить при обработке исключений, чтобы облегчить некоторые из этих проблем. Некоторые из них включают (но, конечно, не ограничиваются):
Option<T>
вместо null
now. Например, только что познакомился с Java 8, взяв подсказку от Guava (и других).
Типичные аргументы заключаются в том, что невозможно определить, какие исключения будут исходить из определенного фрагмента кода (в зависимости от языка) и что они слишком похожи на goto
s, что затрудняет мысленное отслеживание выполнения.
http://www.joelonsoftware.com/items/2003/10/13.html
По этому поводу однозначно нет единого мнения. Я бы сказал, что с точки зрения жесткого программиста на C, такого как Линус, исключения - определенно плохая идея. Однако типичный программист на Java находится в совершенно другой ситуации.
setjmp
/ longjmp
stuff, что довольно плохо.
Исключения неплохие. Они хорошо вписываются в модель RAII C ++, которая является самой элегантной особенностью C ++. Если у вас уже есть код, небезопасный в отношении исключений, то он плох в этом контексте. Если вы пишете действительно низкоуровневое программное обеспечение, такое как ОС Linux, тогда оно плохое. Если вам нравится засорять свой код кучей проверок возврата ошибок, то они бесполезны. Если у вас нет плана управления ресурсами при возникновении исключения (которые предоставляют деструкторы C ++), то они плохие.
Таким образом, отличный вариант использования исключений ...
Допустим, вы работаете над проектом, и каждый контроллер (около 20 различных основных) расширяет один контроллер суперкласса с помощью метода действия. Затем каждый контроллер выполняет кучу вещей, отличных друг от друга, вызывая объекты B, C, D в одном случае и F, G, D в другом случае. Здесь на помощь приходят исключения во многих случаях, когда код возврата был огромным и КАЖДЫЙ контроллер обрабатывал его по-разному. Я взломал весь этот код, выдал правильное исключение из «D», поймал его в методе действий контроллера суперкласса, и теперь все наши контроллеры согласованы. Раньше D возвращал ноль для НЕСКОЛЬКИХ различных случаев ошибок, о которых мы хотели сообщить конечному пользователю, но не могли, а я этого не сделал.
да, мы должны беспокоиться о каждом уровне и любой очистке / утечке ресурсов, но в целом ни у одного из наших контроллеров не было ресурсов, которые нужно было очистить после.
слава богу, у нас были исключения, иначе мне пришлось бы провести огромный рефакторинг и потратить слишком много времени на то, что должно быть простой проблемой программирования.
Теоретически они действительно плохие. В идеальном математическом мире исключительных ситуаций быть не может. Посмотрите на функциональные языки, у них нет побочных эффектов, поэтому у них практически нет источника для обычных ситуаций.
Но реальность - это совсем другое дело. У нас всегда бывают «неожиданные» ситуации. Вот почему нам нужны исключения.
Я думаю, что мы можем рассматривать исключения как синтаксический сахар для ExceptionSituationObserver. Вы просто получаете уведомления об исключениях. Ничего более.
Я думаю, что в Go они представят кое-что, что поможет справиться с «неожиданными» ситуациями. Я могу предположить, что они попытаются сделать так, чтобы это выглядело менее деструктивно как исключения и больше как логика приложения. Но это только мое предположение.
Парадигма обработки исключений C ++, которая составляет частичную основу для Java и, в свою очередь, .net, вводит некоторые полезные концепции, но также имеет некоторые серьезные ограничения. Одно из ключевых намерений разработки обработки исключений - позволить методам гарантировать, что они будут либо удовлетворять своим пост-условиям, либо генерировать исключение, а также гарантировать, что любая очистка, которая должна произойти до выхода метода, произойдет. К сожалению, все парадигмы обработки исключений в C ++, Java и .net не обеспечивают никаких хороших средств обработки ситуации, когда неожиданные факторы препятствуют выполнению ожидаемой очистки. Это, в свою очередь, означает, что нужно либо рискнуть, что все резко остановится, если произойдет что-то неожиданное (подход C ++ к обработке исключения происходит во время раскрутки стека),
Даже если обработка исключений в целом будет хорошей, вполне разумно рассматривать как неприемлемую парадигму обработки исключений, которая не может предоставить хорошие средства для решения проблем, возникающих при очистке после других проблем. Это не значит, что фреймворк не может быть разработан с парадигмой обработки исключений, которая могла бы гарантировать разумное поведение даже в сценариях с множественными отказами, но ни один из ведущих языков или фреймворков пока не может этого сделать.
Я не читал все другие ответы, поэтому об этом уже упоминалось, но есть одна критика в том, что они вызывают разрыв программ в длинных цепочках, что затрудняет отслеживание ошибок при отладке кода. Например, если Foo () вызывает Bar (), который вызывает Wah (), который вызывает ToString (), то случайное нажатие неправильных данных в ToString () в конечном итоге выглядит как ошибка в Foo (), почти полностью не связанной функции.
Хорошо, здесь скучный ответ. Думаю, это действительно зависит от языка. Если исключение может оставить выделенные ресурсы, их следует избегать. В языках сценариев они просто покидают или перепрыгивают через части потока приложения. Само по себе это неприятно, но уход от почти фатальных ошибок с помощью исключений - приемлемая идея.
Для сообщений об ошибках я обычно предпочитаю сигналы об ошибках. Все зависит от API, варианта использования и серьезности, а также от того, достаточно ли ведения журнала. Также я пытаюсь переопределить поведение иthrow Phonebooks()
вместо этого. Идея состоит в том, что «Исключения» часто заходят в тупик, но «Телефонная книга» содержит полезную информацию о восстановлении после ошибок или альтернативных маршрутах выполнения. (Пока не нашел подходящего варианта использования, но продолжайте попытки.)
Для меня вопрос очень простой. Многие программисты неправильно используют обработчик исключений. Больше языковых ресурсов - лучше. Уметь обрабатывать исключения - это хорошо. Одним из примеров неправильного использования является значение, которое должно быть целым числом, которое не проверяется, или другой ввод, который может делиться и не проверяться на деление нуля ... обработка исключений может быть простым способом избежать дополнительной работы и тяжелого мышления, программист может захотеть сделать грязный ярлык и применить обработку исключений ... Утверждение: «профессиональный код НИКОГДА не дает сбоев» может быть иллюзорным, если некоторые из проблем, обрабатываемых алгоритмом, являются неопределенными по своей природе. Возможно, в неизвестных по своей природе ситуациях в игру вступит обработчик исключений. Хорошие методы программирования являются предметом дискуссий.