Re[3]: Result objects - все-таки победили Exceptions?
От: Alekzander Россия  
Дата: 06.01.25 08:17
Оценка: +1
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Alekzander, Вы писали:


A>>
A>>try
A>>{
A>>   const data = LoadData();
A>>   const processedData = ProcessData(data);
A>>   SaveData(processedData);
A>>}
A>>catch
A>>{
A>>    log.Error("Всё пропало");
A>>}
A>>


S>Так нельзя писать. Во-первых, всегда нужно указывать типы исключений. Но беда в том, что вы обычно не думаете об этом — у вас нет четкой картинки в голове какие исключения может вызвать функция. Т.е. вы теряете контроль. А у меня всегда 100% я знаю какие исключения возможны, я всегда это держу в голове для каждой функции.


S>Далее. Исключения нужно отлавливать для каждого шага отдельно — т.е. не все в куче — а везде, на каждом из шагов где могут возникнуть исключения — нужно конкретные отлавливать и обрабатывать, если возмжно.


Исключения нужны, чтобы прятать обработку и "спрямлять" поток выполнения. Это, кстати, роднит их с async/await, которые тоже "спрямляют" поток выполнения. И как и в случае с async/await, тут тоже огромный простор для злоупотреблений. (Все пишут про callback hell. Надо порвать шаблон и как-нибудь написать про async/await hell, который читается хуже, чем парочка честных колбеков).

Иногда не надо скрывать проверки — когда они часть нормального потока выполнения, алгоритмически. Тогда исключения нахрен не нужны. Если делать все проверки в общем catch, будет потеря результатов (как показано в примере выше). А если каждую строчку завернуть в try, просто вывернешь программу наизнанку, с потерей читаемости, и больше ничего.

Между прочим, пример выше я взял из одной статьи на Хабре. Её написал программист, а не астронавт. Говнокодер, правда, зато по космосу не летает. Он так и пишет: я хочу написать простой прямой алгоритм в три строки:

const data = LoadData();
const processedData = ProcessData(data);
SaveData(processedData);


чтобы видеть, что происходит. С обработкой ошибок где-нибудь в подвале, чтоб не мешалась. А противный Go мне не даёт! И поэтому я сконструлил вот такую хохоряшку... (далее следует издевательство над Go).

Что, по-моему, доказывает, что быть говнокодером лучше, чем астронавтом. Говнокодер всё понимает, но делает неправильно. По крайней мере, про спрямление потока он понимает точно, и даже может найти для этого грязные хаки. Со временем жизнь его научит так не делать, а делать правильно. А астронавт... "Воровка никогда не станет прачкой".
Re[3]: Result objects - все-таки победили Exceptions?
От: Privalov  
Дата: 06.01.25 08:30
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>В современных IDE нет никакой проблемы, чтобы указать. IDEA просто не даст откомпилировать метод, в котором выбрасывается checked исключение, потребует либо поставить try-catch, либо добавить throw, и сама это сделает.


Я в курсе. Те, кто писали код в стиле, который я показал, тоже были в курсе. Из всей команды только один парень отмечал все checked исключения в своих методах. Еу и я тоже.
Re[2]: Result objects - все-таки победили Exceptions?
От: sergii.p  
Дата: 06.01.25 08:40
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Исключения, в общем, проще.

PD>У них ИМХО только один недостаток — их нельзя игнорировать. Коды ошибок можно, если знаешь, что тут что с ошибкой, что без ошибки — все равно.

вроде как наоборот. Исключение можно проигнорировать и замолчать (и оно потом выстрелит когда-то в runtime) А тип Result в том же rust невозможно не обработать.
Отредактировано 06.01.2025 8:47 sergii.p . Предыдущая версия .
Re[3]: Result objects - все-таки победили Exceptions?
От: Pavel Dvorkin Россия  
Дата: 06.01.25 08:51
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>вроде как наоборот. Исключение можно проигнорировать и замолчать (и оно потом выстрелит когда-то в runtime) А тип Result в том же rust невозможно не обработать.


Я не имел в виду rust, а имел в виду саму идею возвращать. В C/C++/Pascal/Java/C# вполне можно
With best regards
Pavel Dvorkin
Re: Result objects - все-таки победили Exceptions?
От: Qulac Россия  
Дата: 06.01.25 08:52
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Ну вот рекомендации в новомодных языках: https://docs.flutter.dev/app-architecture/design-patterns/result


S>- все-таки топят за Result objects для бизнес-логики.


S>А ведь это всю цепочку поддерживать. Как-то много лишних букв добавляется.


S>Но! Exception как бы не определены в контрактах. Если ResultObject — это контракт, его понятно что нужно проверить и компилятор даже контролирует эту проверку. А Exception разве что в документации описан, которую могут не читать.


S>А ведь было же хорошее решение — т.н. проверяемые исключения в Java. Когда компилятор требовал проверки того или иного исключения, но так же была возможно обернуть в RuntimeException, если оно утратило смысл бизнес-логики или ожидаемого


S>Но, как оказалось, народ идеи не понял. Так и вернулись к понятным дебилоидам кодам возврата, т.к. проверяемые исключения осилить не смогли. Так же и в Kotlin их решили не делать.


Исключение показывает что мы вышли за область определения контракта объекта, т.е. что при таком состоянии объекта при таких входных данных его поведение не определено и чисто теоретически может быть любым. Это как область определения функции из алгебры. Выброс Exception тут очень удобный механизм. Конечно выброс exception можно использовать не только для защиты контракта объекта но и для других целей, например для наивной валидации, но это уже не лучший прием.
Программа – это мысли спрессованные в код
Re[3]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 06.01.25 12:43
Оценка: +2
J>Ох, держите меня семеро. Это ж надо так на Раст наехать. Вы на нём не пишете? Может стоит сначала пописать/попробовать?
J>В Расте всё это обёрнуто так, что теперь это растаскивают во все языки, включая С++. Означает ли это что Растовцы облажались? Или может наоборот, нащупали что то в пыльной коробке?
Писал на нём ещё до того как это стало мейнстримом
Автор: hi_octane
Дата: 16.12.20
. В 2024-м для продакшена не писал, но с надеждой слежу за обновлениями и иногда "играюсь" с новыми фичами. А знаю что облажались — потому что следил за разработкой с самого начала. Прям следил, читал борьбу разработчиков Borrow Checker, и сколько попыток было сделать нормальные исключения. В итоге получилось смешно — всю кривую "вдохновение->разочарование->использование" растом я прошёл с небольшим опережением. Из-за этого, когда на том же Хабре пошёл пик хайпа и статьи про раст полетели в ежедневном режиме, я был уже на стадии разочарования, и скептически язвил "вы ещё в ракете не смотрели"

J>Сахарок с вопросительным знаком отлично ложится в синтаксис, что это даёт:

Ага. Сначала разрекламировали проверку каждого вызова с match как оправданную плату за доселе невиданную надёжность. Потом сами же поняли что беда, добавили макрос try! в стандартную библиотеку. Потом поняли что и try! — хрень, и добавили "сахарок" с '?'. В 2020-м уже был этот "сахарок", сколько ещё лет нужно чтобы дошёл факт: если к каждому вызову функции нужно применять "сахарок" — то это косяк, ошибка создателей языка. В 90% случаев ошибку надо реально обрабатывать (т.е. что-то осмысленное делать), вовсе не прямо там где она происходит, а сильно выше по стеку. И требование "написать что-то обязательное именно там где ошибка произошла" приводит только к одному — 90% обработок превращаются в бессмысленные затычки и boilerplate.

Кстати, вследствии этого, тенденция очевидна: с каждой итерацией match->try!->'?' — код обработки ошибок всё больше пытаются минимизировать, замести под ковёр, скрыть. Что дальше? Я вот думаю — дальше макрос уровня функции, который сам расставит невидимые '?' везде где они нужны, и, внезапно (нет), мы получим те же исключения, только всё ещё убогонькие и на if-ах под капотом. А ко всему этому в чулане, за дверью которую нельзя открывать, будет ещё безумный родственничек закрыт — panic

И вопрос 5-летней давности всё ещё актуален — кто-то пробовал замерять, сколько сейчас все эти проверки жрут времени по сравнению с кодом без проверок но с исключениями? Или среди фанатов такие вопросы поднимать моветон?

J>Всегда есть опция обработать ошибку на месте, причём без специального очень шумного try/catch/finally/throw синтаксиса а просто получить тип и сматчить его как угодно — часто в одну строку (map, map_or_else, and, or, and_then, ...). Работа с обычной переменной.


Тутъ, читать внизу, начиная с "монадическая обработка"
Автор: hi_octane
Дата: 17.12.20
, и снова за 5 лет лучше по этой части нифига не стало.
Re[3]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 06.01.25 12:55
Оценка: 2 (2) +2
AD>Коды ошибок в расте? Интересно...
Если речь о том что Option или Variant сильно отличаются от return 0 в C, или HRESULT в COM/OLE (тут серая рожа того кто знает ) — то для меня разница исчезающе мала. Современными средствами можно накрутить на почти любой язык проверку что на каждый возврат из функции написан if. То что в Rust такая проверка часть языка — просто деталь реализации.

Если же речь о том что я Rust плохо знаю, то мимо. Я люблю всё новенькое, и растом интересовался задолго до того, как у него появилась минимальная популярность. Основные недостатки прочувствовать успел, за обновлениями слежу. Вот, что кстати грустно, всё ещё актуальное обсуждение аж 5-летней давности
Автор: hi_octane
Дата: 16.12.20
. Несмотря на мелкие улучшения, основные проблемы никуда не делись. Всё также жду Rust 2.

Но и ждать некий "Rust 2" или что-то лучше уже довольно уныло. Глобально, мне кажется, ИИ-шечка сейчас надолго угробит и развитие и создание новых языков программирования. Потому что все новые языки попадут в замкнутый круг: язык новый -> нет кодовой базы для обучения -> нет поддержки ИИ/Copilot -> пишут слишком мало кода -> нет кодовой базы для обучения -> круг замкнулся, мы в ловушке. И по новым фичам существующих языков это также ударит.
Re[2]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 06.01.25 13:08
Оценка:
Q>Исключение показывает что мы вышли за область определения контракта объекта, т.е. что при таком состоянии объекта при таких входных данных его поведение не определено и чисто теоретически может быть любым. Это как область определения функции из алгебры. Выброс Exception тут очень удобный механизм. Конечно выброс exception можно использовать не только для защиты контракта объекта но и для других целей, например для наивной валидации, но это уже не лучший прием.

То что на одном уровне нарушение контракта ацкое нарушение, то на уровне выше вполне себе обрабатываемая ситуация. Например, есть сетевые протоколы, в которых в обе стороны гоняются бесконечные потоки json-пакетов. На уровне парсера — это беда и исключение, ведь битый json это реально нарушение контракта. На уровне сервера — это просто клиент отвалился, закрываем коннект и забываем о нём. На уровне клиента — нужно reconnect и перепослать. А на уровне какой-нибудь утилиты читающей свой конфиг для старт — это снова ад и исключительная ситуация.

Получается разработчикам json-библиотек надо делать 2 версии — одну для тех кому нарушение протокола проблема, а другую для тех для кого это просто ошибочка, не более. Так что ли?
Re[3]: Result objects - все-таки победили Exceptions?
От: Qulac Россия  
Дата: 06.01.25 13:35
Оценка:
Здравствуйте, hi_octane, Вы писали:

Q>>Исключение показывает что мы вышли за область определения контракта объекта, т.е. что при таком состоянии объекта при таких входных данных его поведение не определено и чисто теоретически может быть любым. Это как область определения функции из алгебры. Выброс Exception тут очень удобный механизм. Конечно выброс exception можно использовать не только для защиты контракта объекта но и для других целей, например для наивной валидации, но это уже не лучший прием.


_>То что на одном уровне нарушение контракта ацкое нарушение, то на уровне выше вполне себе обрабатываемая ситуация. Например, есть сетевые протоколы, в которых в обе стороны гоняются бесконечные потоки json-пакетов. На уровне парсера — это беда и исключение, ведь битый json это реально нарушение контракта. На уровне сервера — это просто клиент отвалился, закрываем коннект и забываем о нём. На уровне клиента — нужно reconnect и перепослать. А на уровне какой-нибудь утилиты читающей свой конфиг для старт — это снова ад и исключительная ситуация.


_>Получается разработчикам json-библиотек надо делать 2 версии — одну для тех кому нарушение протокола проблема, а другую для тех для кого это просто ошибочка, не более. Так что ли?


Тут мы подходим к тому, что у ошибок может быть разная сематика. Тот случай который я описал, если возникло исключение — это означает, что в программе баг и правильным способом будет закрыть программу с записью в лог об исключении. В случае если у объекта часть состояния находится за пределами программы(сетевые подключения, файлы и т.д) т.е. то за что программа не может нести ответственность, тут обычно требуется более сложная логика обработки ошибок. Тут мы можем определить разные классы exception и производить обработку для каждого вида ошибок.
Программа – это мысли спрессованные в код
Re: Result objects - все-таки победили Exceptions?
От: Ilya81  
Дата: 06.01.25 13:51
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Ну вот рекомендации в новомодных языках: https://docs.flutter.dev/app-architecture/design-patterns/result


S>- все-таки топят за Result objects для бизнес-логики.


S>А ведь это всю цепочку поддерживать. Как-то много лишних букв добавляется.


S>...


В swift с некотороц версии встроенным result пользоваться удобно, в сочетании с возможностью объявления enum с параметрами. Try catch на каждом шаге — мало того, что громоздко, оно вроде ещё и по части производительности так себе. А если catch только в конце — тогда как понять, что и где произошло, неужели stack разбирать? Т. е. сбоцнул сетевой запрос — есть информация куда, допустим routes объявлены в константах, тогда сравнениваем и узнаём, что там запрашивалос. Но даже ясно что, может быть не очевидно, зачем, даже при условии, что понятно, с чего обработка началас. Ну и какая информация об ошибке в итоге будет — слегка больше, чем просто ошибка? Если, конечно, делать тяп-ляп и в 90% случаев неизвестная ошибка считать приемлемым, то, конечно, но иначе как делать, чтоб информация об ошибке была? Кроме бесконечных rethrow, достоинства которых мне не понятно.
Re[3]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 06.01.25 14:12
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>То что на одном уровне нарушение контракта ацкое нарушение, то на уровне выше вполне себе обрабатываемая ситуация. Например, есть сетевые протоколы, в которых в обе стороны гоняются бесконечные потоки json-пакетов. На уровне парсера — это беда и исключение, ведь битый json это реально нарушение контракта. На уровне сервера — это просто клиент отвалился, закрываем коннект и забываем о нём. На уровне клиента — нужно reconnect и перепослать. А на уровне какой-нибудь утилиты читающей свой конфиг для старт — это снова ад и исключительная ситуация.

_>Получается разработчикам json-библиотек надо делать 2 версии — одну для тех кому нарушение протокола проблема, а другую для тех для кого это просто ошибочка, не более. Так что ли?

Нет, в ракурсе библиотеки JSON (слой утилит) — очевидно что JsonException акцентуированно и оно должно быть проверяемым, его ожидаем.

В точке, где вы используете эту библиотеку — вы уже снимаете акцент и переводите в RuntimeException, JSON-exception уже делаете inner.
=сначала спроси у GPT=
Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 06.01.25 14:14
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>Исключение показывает что мы вышли за область определения контракта объекта, т.е. что при таком состоянии объекта при таких входных данных его поведение не определено и чисто теоретически может быть любым. Это как область определения функции из алгебры. Выброс Exception тут очень удобный механизм. Конечно выброс exception можно использовать не только для защиты контракта объекта но и для других целей, например для наивной валидации, но это уже не лучший прием.


Исключения удобно сделать частью контракта, когда они проверяемые. Нет постоянного бойлерплейт-кода с обертками.
=сначала спроси у GPT=
Re[3]: Result objects - все-таки победили Exceptions?
От: Qulac Россия  
Дата: 06.01.25 15:05
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Qulac, Вы писали:


Q>>Исключение показывает что мы вышли за область определения контракта объекта, т.е. что при таком состоянии объекта при таких входных данных его поведение не определено и чисто теоретически может быть любым. Это как область определения функции из алгебры. Выброс Exception тут очень удобный механизм. Конечно выброс exception можно использовать не только для защиты контракта объекта но и для других целей, например для наивной валидации, но это уже не лучший прием.


S>Исключения удобно сделать частью контракта, когда они проверяемые. Нет постоянного бойлерплейт-кода с обертками.


Я считаю, что это не обязательно.
Программа – это мысли спрессованные в код
Re: Result objects - все-таки победили Exceptions?
От: vsb Казахстан  
Дата: 06.01.25 15:11
Оценка: +4
Считаю, что непроверяемые исключения в общем случае это идеальный способ обработки ошибок и все эти result objects не нужны (могут быть нужны в специфических случаях, но не в общем случае). Проверяемые исключения тоже не нужны, от них проблем больше, чем пользы. По крайней мере в том виде, в котором они в Java.
Отредактировано 06.01.2025 15:14 vsb . Предыдущая версия . Еще …
Отредактировано 06.01.2025 15:13 vsb . Предыдущая версия .
Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 06.01.25 20:26
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Считаю, что непроверяемые исключения в общем случае это идеальный способ обработки ошибок и все эти result objects не нужны (могут быть нужны в специфических случаях, но не в общем случае). Проверяемые исключения тоже не нужны, от них проблем больше, чем пользы. По крайней мере в том виде, в котором они в Java.


А какие проблемы с ними?
=сначала спроси у GPT=
Re[4]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 06.01.25 20:31
Оценка:
Q>Тут мы подходим к тому, что у ошибок может быть разная сематика. Тот случай который я описал, если возникло исключение — это означает, что в программе баг и правильным способом будет закрыть программу с записью в лог об исключении. В случае если у объекта часть состояния находится за пределами программы(сетевые подключения, файлы и т.д) т.е. то за что программа не может нести ответственность, тут обычно требуется более сложная логика обработки ошибок. Тут мы можем определить разные классы exception и производить обработку для каждого вида ошибок

Я в твоём сообщении не могу отделить ошибки от исключений. А хочется определённости — в итоге-то что? Парсеру json, если пришёл битый, что делать — кидать "просто ошибку" или исключение, которое приведёт к закрытию программы? Ну и чтоб два раза не вставать — выход за границу массива это ошибка или исключение, которое должно всё сломать? А невозможность выделить память?

Самый прикол, что решение "исключения существуют, и это нормально, главное что мы их можем перехватывать и обрабатывать" — даёт универсальный и единообразный подход к самым разным проблемам. А вот подход — "у нас в языке исключений нет, ну может чуть-чуть, на донышке" — приводит к тупику. Решение rust как раз в духе, в моём понимании, дибилизма — делайте всего по 2.
Re[4]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 06.01.25 20:31
Оценка:
S>Нет, в ракурсе библиотеки JSON (слой утилит) — очевидно что JsonException акцентуированно и оно должно быть проверяемым, его ожидаем.
S>В точке, где вы используете эту библиотеку — вы уже снимаете акцент и переводите в RuntimeException, JSON-exception уже делаете inner.

Как решается это вопрос, когда исключения есть — я прекрасно знаю. Хотелось бы узнать какое решение этого вопроса предлагают языки без исключений? В Rust есть варианты компиляции без std и с no_panic крейтом. И прекрасно что есть. Но тогда пол экосистемы отваливается, вместе с почти всеми доступными библиотеками. Очень такое себе.

Ну и чтоб веселее было — даже если я напишу код с no_panic, но подгружу плагин, автор которого так не заморачивался — всё. Приехали. Этот плагин мне в одно лицо сложит всю прогу, несмотря на все мои усилия. Очень косячно для языка, который позиционируется как надёжный.
Re[3]: Result objects - все-таки победили Exceptions?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.01.25 20:41
Оценка:
Здравствуйте, johny5, Вы писали:

J>
  • Явно прокидываем ошибку, а не "я не знаю кинет оно или нет" что частенько приводит к сюрпризам в проде и перечитыванию доков. Просто нужно написать "?". При этом задумываешься. Ровно то же самое когда разворачиваешь optional<> (который тоже обретает лютую популярность в С++): всё что нужно сделать, написать "*", но задумываешься, ведь там объекта может и не быть.

    optional<> в С++ — то ещё говно. Откуда ты высосал, что он обретает лютую популярность...
  • Маньяк Робокряк колесит по городу
    Re[3]: Result objects - все-таки победили Exceptions?
    От: hi_octane Беларусь  
    Дата: 06.01.25 21:38
    Оценка:
    S>А какие проблемы с ними?
    Ищи статьи или интервью Хельсберга. Они в MS очень долго решали, потому что сначала идея checked exceptions им тоже казалась очень правильной.

    Самый очевидный пункт: допустим у нас есть сериализатор, который пишет в абстрактный Stream. Этот Stream может быть диском, файлом, памятью, и т.п. Какие исключения объявить сериализатору, если он сам не знает, какой Stream ему дадут на вход? Заставить все Stream кидать абстрактный StreamWriteException? А почему совершенно не имеющие отношения к сериализатору классы, вдруг должны его учитывать и от него зависить, причём зависеть неформально? И это только один из сценариев. А есть ещё версии (например NetworkStream может начать поддерживать новый протокол, и кидать новые типы исключений), и так далее.
    Re[2]: Result objects - все-таки победили Exceptions?
    От: hi_octane Беларусь  
    Дата: 06.01.25 21:40
    Оценка: +2
    I>А если catch только в конце — тогда как понять, что и где произошло, неужели stack разбирать?
    В языках с исключениями никто не заставляет делать catch на каждом шаге. Оптимистично делают кучу связанных друг с другом по смыслу шагов, а если какой-то шаг вылетает — у исключения, в норме, есть вполне информативный тип, по которому проверяют, и решают как обработать. Один метод, который забирает из сети, парсит и сохраняет в файл — может не иметь ни одной строчки try, но бросать 3 типа исключений. И снаружи будет всего один код обработки всего сразу и в одном месте. А этот код уже смотрит: SocketException — проблемы с сетью, FileNotFound — проблемы с файлом, и т.д. Даже если исключение абсолютно неспецифичное — можно юзеру показать текст ошибки, а тот уже с помощью гугла найдёт что сделать, чтобы проблемная функция завершилась успешно. А иногда можно и ничего не делать, повторить операцию, и бывает прокатывает.

    Этот подход, вообще-то, уже отлично проявил себя в базах данных. Начали большую транзакцию, не вышло — ну и откатили. Но выкати БД, с заявой "в нашей идеологии если транзакция не прошла — то ложится вся база", и, мягко говоря, люди не поймут

    Кстати, мало кто знает, почему в Rust трейт std::Error (кто не в теме — аналог интрефеса для ошибки) вполне официально советует сообщение об ошибке давать в нижнем регистре, с минимумом пунктуации. А всё потому, что периодически информацию об ошибке только из сообщения и получают. И это не халтурщики безмозглые, а нормальные прогеры, но вот прилетает откуда-то из недр чего-то ошибка, которую совершенно по канонму наверх передавали с '?'. И никак её отделить нельзя, а обработать именно её особенным образом надо. И тогда, скрипя зубами, делают обработчик по тексту. Вот так вот. Но этого в рекламных материалах не скажут.

    I>Try catch на каждом шаге — мало того, что громоздко, оно вроде ещё и по части производительности так себе.

    Именно что вроде. Как только начинают проверять реальный код — цифры могут быть очень разными. И программы с исключениями, и программы с кодами ошибок пишутся оптимистично — т.е. из расчёта что ошибок будет минимум (а исключений, соответственно, будет мало). При этом в случае кодов ошибок — проверки идут постоянно, и на каждый чих резервируется память в стеке (потому что место для хранения ошибки должно быть всегда). А в случае исключений — каждый чих оборачивать в if и return нет никакой нужды. Что работает быстрее? А зависит от. Мерять конкретный код надо. Когда придумывали миф что исключения медленные — сравнивали не реальные программы, а выброс, условно, миллиона исключений, с миллионом выходов с ошибкой. Но это нереалистичные сценарии. Никто не кидает исключения в цикле миллионами. Код пишется оптимистично. И то что try/catch выписывать в каждой строке неудобно, как раз подстёгивает использовать их для исключительных случаев.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.