Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления.
Или быть может существует более продвинутый механизм наподобие Common Lisp Condition System ?
Здравствуйте, vaa, Вы писали:
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления.
Это достаточно сложный, спорный вопрос со множеством мнений.
Кому то нравится когда контракт возвращаемого результата прописан в результирующем значении функции явно (Result<T>) без скрытого контракта на взвесь "каких то" exceptions, которые код может кинуть (исключения не документируются кодом. Хорошо если в комментах что то написано, но никаких гарантий что не прилетит что то ещё). Кому то нравится обрабатывать ошибку на месте, потому что когда она выплёвывается выше и выше — всё меньше шансов обработать её правильно.
Исключения добавляют второй слой обработки ошибок, и часто становится непонятно, ошибка, например, открытия файла — это исключение или просто вернуть код ошибки. Часто приводит к дупликации API для обоих стилей. Исключения легко забыть необработать, пропущенный stoi() гденть в невинном месте может убить сервер.
То что есть в Расте тоже удобно, ошибки возвращаются явно и если нет нужды обрабатывать на месте, просто прокидываются вверх '?' оператором. Но каждое написание '?' заставляет тебя на секунду задуматься, что можно тут сделать если операция вернула ошибку.
Я на самом деле не против исключений. Просто рассматриваю как альтернативу для сценариев где это действительно нужно. Например в С++ из конструктора никак не вернуть код ошибки и используются исключения. Но в Расте конструкторов нет — всё, обычные функции.
Исключения добавить будет можно всегда, вот тут нагуглился RFC0243 который они ещё обсуждают. Помоему он хорошо резюмируется вот такой цитатой:
No expressivity is added, only convenience. Some object to "there's more than one way to do it" on principle.
Здравствуйте, johny5, Вы писали:
J>Здравствуйте, vaa, Вы писали:
vaa>>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления.
Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток (ну и приложение если поток был главным). Tokio как то хитро это использует и просто выплёвывает запаниковавшие короутины, продолжая работать дальше. Чем то похоже на эксепшионы.
На обработчик panic можно ставить свою процедуру через set_hook. Правда, насколько я понял, это глобальный обработчик и он не может остановить убивание провинившегося потока.
Просветите если чего недопонял, плиз.
Re[3]: Почему в расте отсутствует выброс исключений?
Здравствуйте, johny5, Вы писали:
J>Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток (ну и приложение если поток был главным). Tokio как то хитро это использует и просто выплёвывает запаниковавшие короутины, продолжая работать дальше. Чем то похоже на эксепшионы.
Технически паники по умолчанию практически аналогичны исключениям С++, так же происходит раскрутка стека. Но вот перехватить их можно только для потока целиком. И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс, так что полагаться на паники для обработки ошибок нельзя.
J>На обработчик panic можно ставить свою процедуру через set_hook. Правда, насколько я понял, это глобальный обработчик и он не может остановить убивание провинившегося потока. J>Просветите если чего недопонял, плиз.
Да все правильно, на уровне потока вполне можно перехватывать, перезапускать же придется только ручным созданием нового потока.
Re[3]: Почему в расте отсутствует выброс исключений?
Здравствуйте, johny5, Вы писали:
J>Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток (ну и приложение если поток был главным). Tokio как то хитро это использует и просто выплёвывает запаниковавшие короутины, продолжая работать дальше. Чем то похоже на эксепшионы.
Это они и есть.
J>На обработчик panic можно ставить свою процедуру через set_hook. Правда, насколько я понял, это глобальный обработчик и он не может остановить убивание провинившегося потока. J>Просветите если чего недопонял, плиз.
Здравствуйте, FR, Вы писали:
FR>Технически паники по умолчанию практически аналогичны исключениям С++, так же происходит раскрутка стека. Но вот перехватить их можно только для потока целиком.
Что значит второе предложение?
FR>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс, так что полагаться на паники для обработки ошибок нельзя.
Уточню: это проблема только если мы пишем библиотеку. И даже так мне попадались библиотеки у которых в документации было написано, что требуется panic=unwind. Ещё есть вот такой костыль, чтобы явно это требовать, правда только для найтли: https://stackoverflow.com/a/69273385/7752098
Справедливости ради, в подавляющем большинстве случаев никто обработку ошибок на паниках не делает.
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления. vaa>Или быть может существует более продвинутый механизм наподобие Common Lisp Condition System ?
Здравствуйте, DarkEld3r, Вы писали:
FR>>Технически паники по умолчанию практически аналогичны исключениям С++, так же происходит раскрутка стека. Но вот перехватить их можно только для потока целиком.
DE>Что значит второе предложение?
Означает склероз, про catch_unwind знал, но на практике не использовал.
Ну и в документации прямо не рекомендуется его использовать как замену try/catch, хотя конечно все-равно будут это делать.
FR>>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс, так что полагаться на паники для обработки ошибок нельзя.
DE>Уточню: это проблема только если мы пишем библиотеку. И даже так мне попадались библиотеки у которых в документации было написано, что требуется panic=unwind.
Практически это нарушает универсальность такой библиотеки.
DE>Ещё есть вот такой костыль, чтобы явно это требовать, правда только для найтли: https://stackoverflow.com/a/69273385/7752098
Да полезная вещь.
DE>Справедливости ради, в подавляющем большинстве случаев никто обработку ошибок на паниках не делает.
Ну так или иначе все-таки делают, в конечном счете так и до исключений может дойти
Re[3]: Почему в расте отсутствует выброс исключений?
Здравствуйте, johny5, Вы писали:
J>Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток (ну и приложение если поток был главным). Tokio как то хитро это использует и просто выплёвывает запаниковавшие короутины, продолжая работать дальше. Чем то похоже на эксепшионы.
J>На обработчик panic можно ставить свою процедуру через set_hook. Правда, насколько я понял, это глобальный обработчик и он не может остановить убивание провинившегося потока. J>Просветите если чего недопонял, плиз.
При компиляции можно ставить опцию, когда паника сразу грохает приложение. Поэтому для библиотек панику вместо возврата ошибок делать нельзя.
Для своих приложений в теории — можно. Это действительно очень похоже на исключения. Но это будет как с go, вся стандартная библиотека и все сторонние библиотеки используют другой механизм обработки ошибок, поэтому тут или всё переписывать/оборачивать, или будет в проекте две системы обработки ошибок.
Здравствуйте, FR, Вы писали:
FR>Практически это нарушает универсальность такой библиотеки.
Да, но те примеры, что я помню — это были скорее даже не библиотеки, а фреймворки, причём специализированные (например, substrate), так что это не проблема. Ну и там естественно не вся обработка ошибок на паниках сделана, просто библиотека полагается на возможность их перехватывать.
FR>Ну так или иначе все-таки делают, в конечном счете так и до исключений может дойти
Надеюсь, что нет. (:
Но так-то у паник уже есть всё необходимое для полноценных исключений: у паники можно получить конкретный тип, их можно пробрасывать дальше после перехвата (resume_unwind). Не хватает разве что удобного сахара, но думаю, что его принципиально не делают.
Re[4]: Почему в расте отсутствует выброс исключений?
Здравствуйте, νsb, Вы писали:
νsb>При компиляции можно ставить опцию, когда паника сразу грохает приложение. Поэтому для библиотек панику вместо возврата ошибок делать нельзя. νsb>Для своих приложений в теории — можно. Это действительно очень похоже на исключения. Но это будет как с go, вся стандартная библиотека и все сторонние библиотеки используют другой механизм обработки ошибок, поэтому тут или всё переписывать/оборачивать, или будет в проекте две системы обработки ошибок. νsb>В целом лучше писать код так, как пишут другие.
Ты конечно же всё верно написал, но я всё же добавлю концептуальное насчёт разных механизмов обработки ошибок в приложение.
В любом приложение обязательно должно быть два отдельных способа: обработки ошибок и обработки исключительных ситуаций. Это принципиально разные ситуации с разной необходимой реакций и с разными удобными способами реализации. В первом случае (например при попытке открытия заблокированного файла) приложение должно уведомить (обычно через GUI) пользователя о проблеме и спокойно продолжить выполнение. В этом случае очевидно необходима обработка по месту (часто прямо в той же функции, где открываем файл), поэтому штуки типа классических исключений здесь максимально неудобны (из-за необходимости обкладывать каждый вызов в try/catch). А вот во втором случае (исключительных ситуаций, типа нехватки памяти на машине) приложение обычно просто завершает свою работу, возможно записав что-то в лог (или отправив отчёт на сервер). И здесь наоборот максимально удобен подход с исключениями, в виде одного глобального обработчика на приложение или поток.
Всё вышеописанное касается приложений написанных на любых языках. Но в большинстве языков есть один встроенный механизм обработки, который разработчики из-за незнания/неумения/безысходности используют для всех ситуаций. От этого и возникают все подобные неоднозначности и холивары.
Rust же здесь выгодного отличается наличием двух отдельных механизмов, каждый под свою ситуацию. Паники для обработки исключительных ситуаций и Result для обычных ошибок.
Re[4]: Почему в расте отсутствует выброс исключений?
Здравствуйте, FR, Вы писали:
FR>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс
Но будь осторожен. Я комбо словил, случается именно в вин7 в х32 проге. Там паника это особая инструкция которая кидает исключение, а винда гасит исключения, что произошли внутри вндПрок, просто гасит и продолжает исполнение
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[5]: Почему в расте отсутствует выброс исключений?
Ну лично я с такой постановкой вопроса не согласен. Я пишу серверный софт и у меня 99.99% потенциальных ошибок вроде открытия файла — фатальные. Но не настолько фатальные, чтобы ронять приложение, а настолько фатальные, чтобы размотать стек и на запрос ответил универсальный обработчик ошибок, залоггировав её и вернув 500 код. Мне эта ваша обработка ошибок нафиг не сдалась. Раз в год из тысячи одну такую ошибку нужно обрабатывать специальным образом. Всё остальное время в лучшем случае обработка сводится к доцеплению контекстной информации, чтобы стектрейс пожирней сделать. А чаще всего обработка сводится к отсутствию этой самой обработки.
Поэтому мне что раст, что голанг жутко неудобны и я не понимаю, как на них люди могут что-то писать. А вот обычные тупые исключения вроде питоновских — то, что доктор прописал.
Здравствуйте, vsb, Вы писали:
vsb>Поэтому мне что раст, что голанг жутко неудобны и я не понимаю, как на них люди могут что-то писать. А вот обычные тупые исключения вроде питоновских — то, что доктор прописал.
На днях я попробовал Rocket crate, он как раз даёт именно то что вы сейчас описали. Делаешь unwrap() на открытие/чтение файла и сервер возвращает error 500, продолжая при этом работать.
Re[6]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
vsb>Ну лично я с такой постановкой вопроса не согласен. Я пишу серверный софт и у меня 99.99% потенциальных ошибок вроде открытия файла — фатальные. Но не настолько фатальные, чтобы ронять приложение, а настолько фатальные, чтобы размотать стек и на запрос ответил универсальный обработчик ошибок, залоггировав её и вернув 500 код. Мне эта ваша обработка ошибок нафиг не сдалась. Раз в год из тысячи одну такую ошибку нужно обрабатывать специальным образом. Всё остальное время в лучшем случае обработка сводится к доцеплению контекстной информации, чтобы стектрейс пожирней сделать. А чаще всего обработка сводится к отсутствию этой самой обработки.
Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result.
А насчёт твоего универсального обработчика ошибок... Так он всегда возвращает именно 500? А если где-то надо 404 вернуть или 401, то как тогда? А если случается OOM, то он тоже пытается вернуть запрос (на создание которого опять же нужна память, которой уже нет) с 500?
vsb>Поэтому мне что раст, что голанг жутко неудобны и я не понимаю, как на них люди могут что-то писать. А вот обычные тупые исключения вроде питоновских — то, что доктор прописал.
А что именно неудобно в работе с Result?
Re[7]: Почему в расте отсутствует выброс исключений?
_>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result.
_>А насчёт твоего универсального обработчика ошибок... Так он всегда возвращает именно 500? А если где-то надо 404 вернуть или 401, то как тогда? А если случается OOM, то он тоже пытается вернуть запрос (на создание которого опять же нужна память, которой уже нет) с 500?
Тут согласен, 500 это если всё пошло пузырями, но в целом нужно обрабатывать ошибку и возвращать соответствующую, форматированную страницу.
Re[7]: Почему в расте отсутствует выброс исключений?
Здравствуйте, alex_public, Вы писали:
_>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result.
Нет, удобней это обрабатывать именно исключениями. Т.к. при этом мне не нужно писать никакого кода.
_>А насчёт твоего универсального обработчика ошибок... Так он всегда возвращает именно 500? А если где-то надо 404 вернуть или 401, то как тогда? А если случается OOM, то он тоже пытается вернуть запрос (на создание которого опять же нужна память, которой уже нет) с 500?
Ну вот в 1 случае из 10 000 мне надо вернуть что-то кроме 500. И я для этого напишу эти 4 строчки с обработчиком. А в остальных 9999 случаях не напишу. И даже думать про это не буду, ну кроме как на уровне архитектуры, типа транзакции для БД и тд.
OOM в жаве это отдельная иерархия, но в целом — да, если отвечать на вопрос — пытается. И у него скорей всего получится, т.к. на нижнем уровне там всё используется в пулах преаллоцированных. Хотя, конечно, в условиях OOM всё будет плохо. У меня такой ситуации практически не бывает, но если про это задумываться, я при OOM предпочёл бы просто завершить процесс.
_>А что именно неудобно в работе с Result?
Ну в го неудобно то, что там под каждым вызовом функции еще 3 строки на обработку err. В расте сахарок сделали, но всё равно везде Result-ы мусорят же. Зачем мне это видеть.
Я от раста не требую исключений, язык всё же не про это. Но про неудобство всё равно отметить хотел.
Здравствуйте, T4r4sB, Вы писали:
FR>>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс TB>Но будь осторожен. Я комбо словил, случается именно в вин7 в х32 проге. Там паника это особая инструкция которая кидает исключение, а винда гасит исключения, что произошли внутри вндПрок, просто гасит и продолжает исполнение
Что-то смутно знакомое, с таким гашением, такое кажется и в C++ ловил на winapi, и оно точно есть в VCL с С++ builder.
Re[6]: Почему в расте отсутствует выброс исключений?
Support for the native fast fail mechanism began in Windows 8. Windows operating systems that don't support the fast fail instruction natively will typically treat a fast fail request as an access violation
Behavior type How the system handles uncaught exceptions
1 The system suppresses any uncaught exceptions.
2 The system first terminates the process, and then the Program Compatibility Assistant (PCA) offers to fix it the next time you run the application. You can disable the PCA mitigation by adding a Compatibility section to the application manifest.
3 The system calls the exception filters but suppresses any uncaught exceptions when it leaves the callback scope, without invoking the associated handlers.
The following table shows how a 64-bit version of the Windows operating system, and WOW64, handles uncaught exceptions. Notice that behavior type 2 applies only to the 64-bit version of the Windows 7 operating system and later.
Operating system WOW64 64-bit Windows
Windows XP 3 1
Windows Server 2003 3 1
Windows Vista 3 1
Windows Vista SP1 1 1
Windows 7 and later 1 2
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[5]: Почему в расте отсутствует выброс исключений?
Здравствуйте, alex_public, Вы писали:
_>Всё вышеописанное касается приложений написанных на любых языках. Но в большинстве языков есть один встроенный механизм обработки, который разработчики из-за незнания/неумения/безысходности используют для всех ситуаций. От этого и возникают все подобные неоднозначности и холивары.
_>Rust же здесь выгодного отличается наличием двух отдельных механизмов, каждый под свою ситуацию. Паники для обработки исключительных ситуаций и Result для обычных ошибок.
Чем это отличается? Что мешает в C++? У меня для плюсов, кстати, написан свой Result. Иногда использую. Иногда нет. Иногда использую исключения, когда ошибку не исправить, но можно отловить на каком-то уровне и вывести пользователю или записать в лог и похромать дальше — что-то типа assert'а, только более контролируемого
Здравствуйте, alex_public, Вы писали:
_>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result.
Ошибки могут возникать на всех уровнях, а ловить удобно на одном. Пробрасывать Result руками через 100500 уровней — то ещё удовольствие. Поэтому нет, не то же самое
_>А насчёт твоего универсального обработчика ошибок... Так он всегда возвращает именно 500? А если где-то надо 404 вернуть или 401, то как тогда? А если случается OOM, то он тоже пытается вернуть запрос (на создание которого опять же нужна память, которой уже нет) с 500?
404 или 401 — их возвращают согласно бизнес логике. А 5XX — это как раз "произошла какая-то неведомая херня, как обработать не знаем, мы сломались"
vsb>>Поэтому мне что раст, что голанг жутко неудобны и я не понимаю, как на них люди могут что-то писать. А вот обычные тупые исключения вроде питоновских — то, что доктор прописал.
_>А что именно неудобно в работе с Result?
Его нужно постоянно проверять Вообще, жизнь без постоянной проверки ошибок везде и всегда становится гораздо приятнее. Я вот сейчас пилю проект, там много разных конфигов. Я просто считаю, что там всё хорошо. Если где-то внизу что-то не так, просто кидаю исключение. Обработка ошибок — на верхнем уровне в одном месте, и всё. Сообщаю об ошибке и умываю руки. Потом можно будет переделать — сообщаю об ошибке и гружу дефолтные прибинденные конфиги, и хромаю с ними. Или можно будет добавить отправку репорта на сервер. Или ещё что-то. И всё в одном месте, и без лишних телодвижений на всех уровнях по пробросу ошибок наверх
Я делал как-то свой Result. Что-то типа пары {код_ошибки, результат}, результат валиден, если код ошибки — НЕТ_ОШИБКИ. Иногда использую, но в целом — не очень.
Еще делал мега подсистему для работы с ошибками. Хранил в TLS для каждого потока. Что-то типа GetLastError на стероидах. Можно было делать цепочки ошибок — если где-то внизу произошла ошибка, то можно было не просто её отправить выше, а подвесить ещё свою интерпретацию. До пользователя доходит последнее, но при желании он может посмотреть всю цепочку событий. В принципе, получилось неплохо, до сих пор использую иногда. Там просто другая проблема — получилось уже что-то фреймворка, не всегда его тащит хочется
Здравствуйте, Marty, Вы писали:
TB>>Немного более утомительно, чем TB>>
TB>>auto Result = foo()?;
TB>>
M>А что тут вообще происходит?
Тут происходит ровно то, что я написал в комментарии выше. Проверка наличия ошибки, немедленный возврат ошибки при её наличии, или вернуть результат если ошибки нет.
Кстати, а где в твоём примере разные коды ошибок? Где немедленный возврат из функции в случае ошибки? Больше похоже на Optional.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Кстати, а где в твоём примере разные коды ошибок?
В Result есть поле error
TB>Где немедленный возврат из функции в случае ошибки?
Такого нету, надо через if. Но можно намакросить, будет достаточно коротко
TB>Больше похоже на Optional.
Это он почти и есть, с некоторым дополнениями. Есть ещё Error, который почти как Result, но содержит только код ошибки. Наверно оно аналогично Result<void>, я уже не помню, нафига я его отдельно сделал. Error и Result умеют конвертироваться друг в друга. Есть ещё хитрый оператор !, точно не помню как работает, смотреть лень, он нужен для немного странной записи:
[сcode]
if (auto res = getResult())
{
// Делаем полезное с валидным результатом
}
if (auto res = !getResult())
{
// Результат не валиден
return Error(res);
}
[/сcode]
А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
Здравствуйте, Marty, Вы писали:
M>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[10]: Почему в расте отсутствует выброс исключений?
Трюк с оператором ! вот какой — он просто ставит в поле ошибки флаг, оператор преобразования в bool его сбрасывает, т.е. первая проверка на истинность в if'е восстанавливает нормальный код ошибки
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Error operator!() const
{
return Error(result | errors::invertFlag);
}
Ну, и ещё я не доделал случай, когда конструктор по умолчанию недоступен
errors.h
#pragma once
//----------------------------------------------------------------------------
/*! \file
\brief Ошибки, и связанные с ними определения
*/
//----------------------------------------------------------------------------#include"umba/preprocessor.h"#include"umba/stl.h"#if defined(UMBA_WIN32_USED)
#include <stdio.h>
#endif//----------------------------------------------------------------------------
//----------------------------------------------------------------------------namespace umba{
typedef uint32_t ErrorCode; //!< Сырой целочисленный тип кода ошибки
//----------------------------------------------------------------------------namespace errors
{
const ErrorCode errorFlag = 0x80000000; //!< Флаг - ошибкаconst ErrorCode invertFlag = 0x40000000; //!< Флаг инверсииconst ErrorCode errorSubsystemMask = 0x0F000000; //!< Маска подсистемыconst ErrorCode errorSubsystemUmba = 0x00000000; //!< Подсистема UMBAconst ErrorCode errorSubsystemWindows = 0x01000000; //!< Подсистема Windows - проброс виндового кода ошибкиconst ErrorCode errorSubsystemPosix = 0x02000000; //!< Подсистема Posix - проброс Posix ошибки - Linux ошибка или (под Windows) - ошибка CRTconst ErrorCode errorSubsystemQt = 0x03000000; //!< Подсистема Qt - ошибка фреймворка Qt
//! Код ошибки из виндового кодаinline ErrorCode makeFromWindows( ErrorCode errorCode )
{
return errorFlag | errorSubsystemWindows | (errorCode & ~(errorSubsystemMask | errorFlag));
}
//! Код ошибки из Posix кодаinline ErrorCode makeFromPosix( ErrorCode errorCode )
{
return errorFlag | errorSubsystemPosix | (errorCode & ~(errorSubsystemMask | errorFlag));
}
//! Код ошибки из Qt кодаinline ErrorCode makeFromQt( ErrorCode errorCode )
{
return errorFlag | errorSubsystemQt | (errorCode & ~(errorSubsystemMask | errorFlag));
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifdef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE
#endif
#ifdef UMBA_DECLARE_ERROR_CODE_ALIAS
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
#endif/* info */#define UMBA_DECLARE_ERROR_CODE(name, code, info) \
const ErrorCode name = code;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info) \
const ErrorCode name = code;
#endif/* DOXYGEN_SHOULD_SKIP_THIS */#include"zz_error_codes.h"#ifndef DOXYGEN_SHOULD_SKIP_THIS
#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
} // namespace errors
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------template <typename TValue>
struct Result;
#if 0
//! Конструктор по умолчанию
//! Конструктор из объекта "ошибка"
//! Конструктор из кода ошибки и результата (не все ошибки - фатальные)
//! Конструктор копирования
//! Конструктор перемещения#endif//! Плейс-холдер для кода ошибкиclass Error
{
public:
mutable ErrorCode result; //!< Код ошибки
//! Конструктор по умолчанию - создаёт "Ок - Нет ошибки"
Error() : result(errors::ok) {}
//! Конструктор из кода ошибки
Error(ErrorCode ec) : result(ec) {}
//! Конструктор копирования
Error(const Error &e) : result(e.result) {}
//! Оператор присваивания кода ошибки
Error& operator=(const ErrorCode &e) { result = e; return *this; }
//! Оператор присваивания
Error& operator=(const Error &e) { result = e.result; return *this; }
//! Преобразование в Resulttemplate <typename TValue>
Error( const Result<TValue> &r );
//! Проверка на сбойbool isFail() const
{
return (result & errors::errorFlag) ? true : false;
}
//! Проверка на успехbool isSuccess() const
{
return !isFail();
}
//! Проверка на полный успехbool isClean() const
{
return result == 0;
}
//! Использование в операторе if и сравнение с bool
/*! Проверка на успешность вызова:
if (auto a = someFunction()) { Ветка по успеху }
*/
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Error operator!() const
{
return Error(result | errors::invertFlag);
}
#ifdef UMBA_MCU_USED
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// errors:: #define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return UMBA_STRINGIFY(name);
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает строку с сообщением об ошибкеconst char* what() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
return"Unknown error";
}
};
}
//! Возвращает Unicode строку сообщением об ошибкеconst wchar_t* whatWide() const
{
return L"Some error";
}
#else
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// errors:: #define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return info;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает строку с сообщением об ошибкеconst char* what() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
#if defined(_WIN32) || defined(WIN32)
ErrorCode cleanCode = result & ~(errorSubsystemMask|errorFlag|invertFlag);
const char* fmt = "Unknown error %d (0x%08X)";
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: fmt = "Umba error %d (0x%08X)"; break;
case errorSubsystemWindows: fmt = "Windows error %d (0x%08X)"; break;
case errorSubsystemPosix: fmt = "Posix error %d (0x%08X)"; break;
case errorSubsystemQt: fmt = "Qt error %d (0x%08X)"; break;
//default: return "Unknown error";
};
#if defined(UMBA_CXX_HAS_STD11)
thread_local
#else
#if defined(_MSC_VER)
__declspec( thread )
#endif
#endif
static char buf[64];
sprintf(buf, fmt, cleanCode, result);
return buf;
#else
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: return"Umba error";
case errorSubsystemWindows: return"Windows error";
case errorSubsystemPosix: return"Posix error";
case errorSubsystemQt: return"Qt error";
default: return"Unknown error";
};
#endif
}
};
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
//errors::#define UMBA_DECLARE_ERROR_CODE(name, code, info) \
case name : return L##info;
#define UMBA_DECLARE_ERROR_CODE_ALIAS(name, code, info)
#endif/* DOXYGEN_SHOULD_SKIP_THIS */
//! Возвращает Unicode строку сообщением об ошибкеconst wchar_t* whatWide() const
{
switch(result & ~errors::invertFlag)
{
using namespace errors;
#include"zz_error_codes.h"#undef UMBA_DECLARE_ERROR_CODE
#undef UMBA_DECLARE_ERROR_CODE_ALIAS
default:
{
#if defined(_WIN32) || defined(WIN32)
ErrorCode cleanCode = result & ~(errorSubsystemMask|errorFlag|invertFlag);
const wchar_t* fmt = L"Unknown error %d (0x%08X)";
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: fmt = L"Umba error %d (0x%08X)"; break;
case errorSubsystemWindows: fmt = L"Windows error %d (0x%08X)"; break;
case errorSubsystemPosix: fmt = L"Posix error %d (0x%08X)"; break;
case errorSubsystemQt: fmt = L"Qt error %d (0x%08X)"; break;
//default: return "Unknown error";
};
#if defined(UMBA_CXX_HAS_STD11)
thread_local
#else
#if defined(_MSC_VER)
__declspec( thread )
#endif
#endif
static wchar_t buf[64];
swprintf(buf, fmt, cleanCode, result);
return buf;
#else
switch(result&errorSubsystemMask)
{
case errorSubsystemUmba: return L"Umba error";
case errorSubsystemWindows: return L"Windows error";
case errorSubsystemPosix: return L"Posix error";
case errorSubsystemQt: return L"Qt error";
default: return L"Unknown error";
};
#endif
}
};
}
#endif
}; // struct Error
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//! Проверка двух Error между собой на равенствоinline
bool operator==(Error e1, Error e2)
{
return e1.result == e2.result;
}
//------------------------------
//! Проверка двух Error между собой на неравенствоinline
bool operator!=(Error e1, Error e2)
{
return e1.result != e2.result;
}
//------------------------------
//! Проверка Error и ErrorCode на равенствоinline
bool operator==(Error e1, ErrorCode e2)
{
return e1.result == e2;
}
//------------------------------
//! Проверка Error и ErrorCode на неравенствоinline
bool operator!=(Error e1, ErrorCode e2)
{
return e1.result != e2;
}
//------------------------------
//! Проверка ErrorCode и Error на равенствоinline
bool operator==(ErrorCode e1, Error e2)
{
return e1 == e2.result;
}
//------------------------------
//! Проверка ErrorCode и Error на неравенствоinline
bool operator!=(ErrorCode e1, Error e2)
{
return e1 != e2.result;
}
//------------------------------
//! Проверка Error и bool на равенство - Error равен true, когда он успешенinline
bool operator==(Error e1, bool e2)
{
return e1.isSuccess() == e2;
}
//------------------------------
//! Проверка Error и bool на неравенство - Error равен true, когда он успешенinline
bool operator!=(Error e1, bool e2)
{
return e1.isSuccess() != e2;
}
//------------------------------
//! Проверка bool и Error на равенство - Error равен true, когда он успешенinline
bool operator==(bool e1, Error e2)
{
return e1 == e2.isSuccess();
}
//------------------------------
//! Проверка bool и Error на неравенство - Error равен true, когда он успешенinline
bool operator!=(bool e1, Error e2)
{
return e1 != e2.isSuccess();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//! Обобщенный результат
/*! Данный класс позволяет возвращать код результата (ошибки)
одновременно с полезной нагрузкой. Когда полезная нагрузка не нужна,
следует использовать класс Error
*/template <typename TValue>
struct Result : public Error
{
public:
TValue value; //!< Значение результата
//! Конструктор по умолчанию
Result( )
: Error()
, value()
{}
//! Конструктор из объекта "ошибка"
Result( const Error &e )
: Error(e.result)
, value()
{}
//! Конструктор из кода ошибки и результата (не все ошибки - фатальные)
Result( ErrorCode ec, const TValue &tv )
: Error(ec)
, value(tv)
{}
//! Конструктор копирования
Result( const Result &r )
: Error(r.result)
, value(r.value)
{}
#if defined(UMBA_CXX_HAS_STD11)
//! Конструктор перемещения
Result(Result &&r)
: Error(r.result)
, value()
{
value = std::move(r.value);
}
#endif//! Конструктор из значения результата
Result( const TValue &tv )
: Error(0)
, value(tv)
{}
/*
explicit operator Error() const
{
return Error(result);
}
*/
//! Оператор присваивания
Result& operator=(const Result &r)
{
if (&r==this) return *this;
result = r.result;
value = r.value;
return *this;
}
#if defined(UMBA_CXX_HAS_STD11)
//! Присваивающий оператор присваивания
Result& operator=(Result &&r)
{
if (&r==this) return *this;
result = std::move(r.result);
value = std::move(r.value);
return *this;
}
#endif//! Использование в операторе if и сравнение с bool
/*! Проверка на успешность вызова:
if (auto a = someFunction()) { Ветка по успеху }
if (auto a = !someFunction()) { Ветка по неудаче }
*/
UMBA_EXPLICIT
operator bool() const
{
if ((result&errors::invertFlag)==0) // invert flag not setreturn isSuccess();
result &= ~ errors::invertFlag;
return isFail();
}
//! Использование в операторе if с инверсией
/*! Проверка на фейл вызова:
if (auto a = !someFunction()) { Ветка по неудаче }
*/
Result operator!() const
{
return Result( result | errors::invertFlag, value );
}
}; // struct Resulttemplate <typename TValue>
inline
Error::Error( const Result<TValue> &r ) : result(r.result)
{
}
} // namespace umba
zz_error_codes.h
/*! \file
\brief Коды ошибок
*/
UMBA_DECLARE_ERROR_CODE (ok , 0, "Ok, no error")
UMBA_DECLARE_ERROR_CODE_ALIAS(success , ok, "Ok, operation successfully completed")
UMBA_DECLARE_ERROR_CODE_ALIAS(done , ok, "Ok, operation done")
UMBA_DECLARE_ERROR_CODE_ALIAS(no_error , ok, "Ok, no error")
UMBA_DECLARE_ERROR_CODE_ALIAS(bool_true , ok, "True condition")
UMBA_DECLARE_ERROR_CODE (already , 1, "Ok, but already done")
UMBA_DECLARE_ERROR_CODE (need_reopen , 2, "Device need to be reopened before changes will take effect")
UMBA_DECLARE_ERROR_CODE(bool_false , 0 | errorFlag, "False condition")
UMBA_DECLARE_ERROR_CODE(fail , 1 | errorFlag, "Generic fail")
UMBA_DECLARE_ERROR_CODE(not_implemented , 2 | errorFlag, "Method/function not implemented")
UMBA_DECLARE_ERROR_CODE(would_block , 3 | errorFlag, "Blocking mode required / Operation would block")
UMBA_DECLARE_ERROR_CODE(connection_in_progess , 4 | errorFlag, "Asynchonous connection in progress")
UMBA_DECLARE_ERROR_CODE(shutdown_in_progess , 5 | errorFlag, "Asynchonous shutdown in progress")
UMBA_DECLARE_ERROR_CODE(wrong_io_mode , 6 | errorFlag, "Wrong IO mode - attempt to read on write only, attemp to write to read only etc")
UMBA_DECLARE_ERROR_CODE(bus_addr_not_bound , 7 | errorFlag, "Attempt to write to bus stream, but device bus address not bound")
UMBA_DECLARE_ERROR_CODE(invalid_bus_addr , 8 | errorFlag, "Invalid bus adress taken")
UMBA_DECLARE_ERROR_CODE(unknown_bus_addr , 9 | errorFlag, "Unknown bus adress")
UMBA_DECLARE_ERROR_CODE(not_required , 10 | errorFlag, "Operation not required")
UMBA_DECLARE_ERROR_CODE(permission_denied , 11 | errorFlag, "Permission denied")
UMBA_DECLARE_ERROR_CODE(not_found , 12 | errorFlag, "Requested object not found")
UMBA_DECLARE_ERROR_CODE(device_not_opened , 13 | errorFlag, "Device is not opened")
UMBA_DECLARE_ERROR_CODE(device_busy , 14 | errorFlag, "Device is busy (temporary)")
UMBA_DECLARE_ERROR_CODE(device_already_online , 15 | errorFlag, "Device is already opened/online")
UMBA_DECLARE_ERROR_CODE(device_required_online , 16 | errorFlag, "Device must be online to perform this operation")
UMBA_DECLARE_ERROR_CODE(device_removed , 17 | errorFlag, "Device unexpectedly removed from the system")
UMBA_DECLARE_ERROR_CODE(read_failed , 18 | errorFlag, "Read operation failed (generic read error)")
UMBA_DECLARE_ERROR_CODE(write_failed , 19 | errorFlag, "Write operation failed (generic write error)")
UMBA_DECLARE_ERROR_CODE(not_supported , 20 | errorFlag, "Operation is not supported")
UMBA_DECLARE_ERROR_CODE(prohibited , 21 | errorFlag, "Operation prohibited by the underlying layer")
UMBA_DECLARE_ERROR_CODE(timed_out , 22 | errorFlag, "Operation was timed out")
UMBA_DECLARE_ERROR_CODE(invalid_param , 23 | errorFlag, "Invalid parameter taken")
UMBA_DECLARE_ERROR_CODE(invalid_state , 24 | errorFlag, "Object is in invalid state")
UMBA_DECLARE_ERROR_CODE(invalid_prerequisites , 25 | errorFlag, "Invalid prerequisites")
UMBA_DECLARE_ERROR_CODE(protocol_not_ready , 26 | errorFlag, "Protocol not ready to perform this operation")
UMBA_DECLARE_ERROR_CODE(too_much_data , 27 | errorFlag, "Too much data")
UMBA_DECLARE_ERROR_CODE(stream_not_assigned , 28 | errorFlag, "Stream not assigned")
UMBA_DECLARE_ERROR_CODE(unknown_interface , 29 | errorFlag, "Unknown (unsupported) interface")
UMBA_DECLARE_ERROR_CODE(header_crc_error , 30 | errorFlag, "Header CRC error")
UMBA_DECLARE_ERROR_CODE(crc_error , 31 | errorFlag, "CRC error")
UMBA_DECLARE_ERROR_CODE(out_of_sync , 32 | errorFlag, "Protocol is out of sync")
UMBA_DECLARE_ERROR_CODE(invalid_protocol_headeer , 33 | errorFlag, "Protocol header contains invalid data")
UMBA_DECLARE_ERROR_CODE(wellknown_addr_not_supported , 34 | errorFlag, "Wellknown address not supperted")
UMBA_DECLARE_ERROR_CODE(temporary_failure , 35 | errorFlag, "Temporary failure (often hardware)")
UMBA_DECLARE_ERROR_CODE(too_many_opened , 36 | errorFlag, "Too many opened files (sockets, handles etc)")
UMBA_DECLARE_ERROR_CODE(error_aborted , 37 | errorFlag, "The operation was aborted")
UMBA_DECLARE_ERROR_CODE(open_error , 38 | errorFlag, "Open operation generic error")
UMBA_DECLARE_ERROR_CODE(resize_error , 39 | errorFlag, "Can't resize stream/IO device (is it pipe etc?)")
UMBA_DECLARE_ERROR_CODE(not_seekable , 40 | errorFlag, "IO device not seekable")
UMBA_DECLARE_ERROR_CODE(file_operation_error , 41 | errorFlag, "File operation error (remove/rename/copy/etc)")
UMBA_DECLARE_ERROR_CODE(eof_reached , 42 | errorFlag, "End of file reached")
//UMBA_DECLARE_ERROR_CODE( , | errorFlag, "")
Здравствуйте, T4r4sB, Вы писали:
M>>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
TB>Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Здравствуйте, Marty, Вы писали:
M>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
M>>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
А, ясно.
А я делал немного не так — у меня success состояние может содержать какое-то значение, типа всё хорошо, но были кой какие проблемки. В COM-овском HRESULT стырил идею
Здравствуйте, vaa, Вы писали:
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления. vaa>Или быть может существует более продвинутый механизм наподобие Common Lisp Condition System ?
На мой взгляд совершенно неудобный. По крайней мере в C++ это все просто ужасно,
как будто пишешь обработчик прерываний, который должен работать в условиях возможных вложенных прерываний,
а что произойдет если `operator=` выбросить исключение, а что произойдет если при передаче параметра в функцию,
но до ее вызова будет исключение и т.д. и т.п.
Предсказуемость и понимание того, что будет если эта функция завершиться с ошибкой,
а также контроль во время компиляции того, что `Result` будет обработан,
дорого стоит.
Ну плюс отсутствие исключение позволяет оптимизатору компилятора действительно в некоторых
случаях абстракции которые как бы "zero cost" превращать в действительно "zero cost".
Был пару лет назад доклад про `std::unique_ptr` и там были фундаментальные проблемы к сведению его
к простому указателю на в генерируемом ассебмлерном коде в том числе и из-за исключений.
Re[11]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
Либо более высокоуровневая ошибка должна реализовывать трейт From для исходной ошибки. Тогда просто оператора "?" достаточно безо всяких map_err (хотя и map_err тоже полезная штука). В крейтах thiserror и anyhow есть полезные макросы и типы для того, чтобы не заморачиваться этим вручную.
Здравствуйте, ArtDenis, Вы писали:
AD>Либо более высокоуровневая ошибка должна реализовывать трейт From для исходной ошибки. Тогда просто оператора "?" достаточно безо всяких map_err (хотя и map_err тоже полезная штука). В крейтах thiserror и anyhow есть полезные макросы и типы для того, чтобы не заморачиваться этим вручную.
Да, хотел как раз об этом написать, обычно пишутся автоконверторы ошибок с помощью thiserror макросов, либо всё коллапсируется в единую строковую ошибку с колстеком (anyhow).
Первое нужно если хочется в интерфейсе какой то набор возможных ошибок, например io::error, db::error, и туда автоматически склеивать разнообразные ошибки с нижних уровней. Второе, если не хочется парится а просто вернуться при любой ошибке. Оператор '?' работает и там и там.
Re[5]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, FR, Вы писали:
FR>>И кроме того есть режим компиляции panic = abort и при его использовании любая паника в любом потоке прибивает процесс TB>Но будь осторожен. Я комбо словил, случается именно в вин7 в х32 проге. Там паника это особая инструкция которая кидает исключение, а винда гасит исключения, что произошли внутри вндПрок, просто гасит и продолжает исполнение
И..? А что ожидалось? Паникам и исключениям нельзя пересекать языковые барьеры, так как ОС не знает о Rust, а Rust — об исключениях ОС и С++.
Re[6]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Marty, Вы писали:
M>>А в расте оператор ?, как я понял, проверяет на ошибку и делает return из текущей функции? Ну может и удобно, но как я понимаю, у всей иерархии тип результата одинаковый? Это не особо часто таки бывает. Или как оно работает?
TB>Ты имеешь в виду наверное не "тип результата", а "тип ошибки"? Да, если он разный, то проблемка. Ну либо вручную делать
TB>
TB>auto Result = foo().map_err(|e| foo_error_to_bar_error(e))?
TB>
TB>Либо на каком-то уровне иерархии возвращать Box<dyn Err>, ну и типа сразу наиболее общий тип ошибки возвращать.
Не только. try работает с конвертацией типрв, поэтому если BarError умеет создаваться из FooError (impl From<FooError> for BarError), то ? работает прозрачно: foo()?;
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, Marty, Вы писали:
M>>Ну, я не до конца таки там понял, я думал, что есть какой-то стандартный тип ошибки, который этот оператор умеет проверять, и к нему в паре идёт возвращаемое значение. И вот оно может быть разного типа на разных уровнях иерархии
TB>Result это такой TaggedUnion, который либо ошибка, либо результат. Тип ошибки и результата не особо ограничиваются. А оператор заточен именно вот Result из стандартной библиотеки.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, flаt, Вы писали:
F>>И..? А что ожидалось?
TB>А почему нельзя просто завалить приложение?! Почему для х64 так и сделано, а для х86, запускаемом на х64, нет?
Здравствуйте, flаt, Вы писали:
F>Это они выпилили поддержку Windows 7 не так давно (против чего было много возмущений, но в расте сидят хиппи и им лень поддерживать старые ОС).
Не хиппи, а хипстеры, не путай.
Старадающие той же хернёй, что и хаскеллисты, только в мягкой форме — когда теоретическая чистота идеи, даже если она постоянно вставляет палки в колёса в реальном коде, важнее практичности.
Просто у функциональщиков это совсем упоротость за гранью, но у растаманов тоже есть.
Здравствуйте, johny5, Вы писали:
J>Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток (ну и приложение если поток был главным).
Ваш раст делали идиоты. И в итоге сделали говно с дичайшими и лютейшими граблями. Постараюсь обходить и раст, и что-либо на нём сделанное, и что-либо использовавшее библиотеки на расте, или что-либо зависящее от чего-нибудь написанного на расте.
Если от меня когда-либо будет зависеть выбор софта, то я постараюсь прошерстить использованные инструменты и либы и рекомендую отказаться от кривого выбора при обнаружении. Себе дороже будет такое пропустить.
Здравствуйте, DarkEld3r, Вы писали:
DE>Я так понимаю, что подробностей и примеров граблей не будет?..
Беззнаковые индексы и общий фанатизм по "типам, диапазон которых лучше всего представляет допустимые в данном контексте значения", что на деле только вынуждает пердолиться с кастами каждый раз когда нужно что-то сложить, или, боже упаси, вычесть.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[5]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
DE>Я так понимаю, что подробностей и примеров граблей не будет?..
А какие именно подробности тебе нужны? Незакрытых файлов, не освобождённых объектов синхронизации и инконсистентного состояния программы не достаточно? По-моему вполне. Потоки убивать нельзя!
Ф>>ЗЫ: точно такие же идиоты в шарпе https://learn.microsoft.com/ru-ru/dotnet/api/system.threading.thread.abort?view=net-7.0 DE>В чём суть претензии? В наличии этого метода в принципе или в том, что аборт можно перехватить?
Странно, что ты только про этот метод спросил, а вот про TerminateThread() умолчал. Претензия в том, что эта дрянь может кинуть исключение там, где его изначально в принципе не предполагалось, а то что оно может прервать статический конструктор и прервать инициализацию статических полей класса — это дикий трэш. Просто представь себе, что у тебя инициализируется глобальный экземпляр какой-нибудь сложной хрени, и в этот момент прилетает ThreadAbort — всё, приплыли: ты потом в жизни не разберёшься почему программа погоду на Марсе добывает вместо того чтобы работать.
Там кроме того что ты можешь написать на эту тему в самой BCL попадается, например:
Здравствуйте, T4r4sB, Вы писали:
TB>Беззнаковые индексы и общий фанатизм по "типам, диапазон которых лучше всего представляет допустимые в данном контексте значения", что на деле только вынуждает пердолиться с кастами каждый раз когда нужно что-то сложить, или, боже упаси, вычесть.
Я бы не сказал, что эти "грабли" исключительно растовая проблема. Да в целом тема достаточно спорная.
Re[7]: Почему в расте отсутствует выброс исключений?
Здравствуйте, Философ, Вы писали:
Ф>А какие именно подробности тебе нужны? Незакрытых файлов, не освобождённых объектов синхронизации и инконсистентного состояния программы не достаточно? По-моему вполне. Потоки убивать нельзя!
У меня складывается впечатление, что ты не разобрался, но уже начал громко орать. В расте нельзя "убить поток". Ну кроме как получив нативный хендл и сделав это средствами системного апи, но это можно сделать везде, где можно получить этот самый хендл и к языку оно отношения не имеет.
Паника, о которой говорилось выше — это просто "исключение", которое разматывает стек (есть альтернативный режим в котором паника — это немедленное завершение приложения). Никаких проблем при этом не возникает. Можно, конечно, писать код небезопасный к исключениям, но это везде так. И я бы сказал, что в расте это немного сложнее: повсеместно используется RAII и глобальное состояние не поощряется.
Когда-то давно панику можно было перехватить исключительно на границе потоков, а не в произвольный момент. Из-за этого периодически всплывает связь паники с потоками.
Ф>Странно, что ты только про этот метод спросил, а вот про TerminateThread() умолчал.
Честно говоря, я вообще не понял на что по ссылке надо обращать внимание, поэтому и переспросил. С .NET дела практически не имел. Спорить с тем, что прибить поток в произвольный момент ни к чему хорошему не приведёт я не буду.
Re[7]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
DE>У меня складывается впечатление, что ты не разобрался, но уже начал громко орать.
Я не разбирался — не пишу на расте, просто прочитал то что написал johny5. Написал он вот так:
Кстате обнаружил для себя panic!, оказывается он не убивает приложение а только текущий поток ...
DE>Паника, о которой говорилось выше — это просто "исключение", которое разматывает стек (есть альтернативный режим в котором паника — это немедленное завершение приложения).
Ах разматывает!!
DE>Когда-то давно панику можно было перехватить исключительно на границе потоков, а не в произвольный момент. Из-за этого периодически всплывает связь паники с потоками.
Ах можно перехватить!
DE>Честно говоря, я вообще не понял на что по ссылке надо обращать внимание, поэтому и переспросил. С .NET дела практически не имел. Спорить с тем, что прибить поток в произвольный момент ни к чему хорошему не приведёт я не буду.
По ссылке написано, что сейчас оно исключение кидает и больше ничего не делает.
Caution
Thread.Abort is not supported and throws PlatformNotSupportedException.
По-моему его вообще изначально не надо было добавлять — кроме вреда от него ничего больше не было.
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
Ф>А какие именно подробности тебе нужны? Незакрытых файлов, не освобождённых объектов синхронизации и инконсистентного состояния программы не достаточно? По-моему вполне. Потоки убивать нельзя!
Объекты при этом освобождаются, файлы закрываются, объекты синхронизации "отравляются", инконсистентного состояния не наблюдается
Здравствуйте, DarkEld3r, Вы писали:
DE>Ну а так в интернете чего только не пишут, не всё из этого правда.
Ф>>Ах разматывает!! Ф>>Ах можно перехватить!
DE>Не пойму к чему этот сарказм. Всё ещё видишь какие-то проблемы с паникой и/или потоками в расте?
Я вот даже не знаю чему тут теперь верить:
Объекты при этом освобождаются, файлы закрываются, объекты синхронизации "отравляются", инконсистентного состояния не наблюдается
Здравствуйте, vsb, Вы писали:
_>>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result. vsb>Нет, удобней это обрабатывать именно исключениями. Т.к. при этом мне не нужно писать никакого кода.
Твоя фраза подразумевает, что при обработке с исключениями не надо писать какой-то код, который надо писать при обработке ошибок через Result в Rust'e. И т.к. это очевидно не код самой реакции на ошибку (который естественно надо писать в любом подходе и языке), то тогда о чём собственно речь? Или ты считаешь "дополнительным кодом" несколько знаков вопроса?
vsb>OOM в жаве это отдельная иерархия, но в целом — да, если отвечать на вопрос — пытается. И у него скорей всего получится, т.к. на нижнем уровне там всё используется в пулах преаллоцированных. Хотя, конечно, в условиях OOM всё будет плохо. У меня такой ситуации практически не бывает, но если про это задумываться, я при OOM предпочёл бы просто завершить процесс.
Именно! И как раз для таких ситуаций исключения (или паники) подходят идеально.
_>>А что именно неудобно в работе с Result? vsb>Ну в го неудобно то, что там под каждым вызовом функции еще 3 строки на обработку err. В расте сахарок сделали, но всё равно везде Result-ы мусорят же. Зачем мне это видеть.
Ну тут есть довольно простой ответ, через встречный вопрос. ))) Вот допустим ты используешь механизм исключений для обработки обычных ошибок. Можешь ли ты взяв произвольную функцию в коде точно перечислить список ошибок, который она может вернуть?
Re[6]: Почему в расте отсутствует выброс исключений?
Здравствуйте, Marty, Вы писали:
_>>Всё вышеописанное касается приложений написанных на любых языках. Но в большинстве языков есть один встроенный механизм обработки, который разработчики из-за незнания/неумения/безысходности используют для всех ситуаций. От этого и возникают все подобные неоднозначности и холивары. _>>Rust же здесь выгодного отличается наличием двух отдельных механизмов, каждый под свою ситуацию. Паники для обработки исключительных ситуаций и Result для обычных ошибок. M>Чем это отличается? Что мешает в C++? У меня для плюсов, кстати, написан свой Result. Иногда использую. Иногда нет. Иногда использую исключения, когда ошибку не исправить, но можно отловить на каком-то уровне и вывести пользователю или записать в лог и похромать дальше — что-то типа assert'а, только более контролируемого
В принципе я уже не раз говорил, что C++ и Rust почти что эквивалентные. Есть микронюансы, которые можно сделать на одном языке и нельзя на другом, а 99% возможностей пересекаются. Но это если говорить именно о возможности. Если же говорить об удобстве или традициях (существует ли общепринятая для языка практика под данному вопросу или же каждый делает как хочет), то тут уже возможна большая разница.
Если вернуться конкретно к вопросу работы с Result, то тут всё в точности как я описал выше. Создать подобный тип можно в обоих языках, но в Rust с ним будет удобнее работать из-за наличия двух синтаксических сахаров: развитого сопоставления с образцом и встроенного оператора "?". И плюс данный подход является общепринятым в сообществе, так что во всех библиотеках ты можешь видеть именно его и есть способы бесшовного автоматического склеивания их всех в единую стройную систему.
Re[9]: Почему в расте отсутствует выброс исключений?
Здравствуйте, alex_public, Вы писали:
_>>>Ну, то что ты описал — это как раз случай не исключительной ситуации, а обычной обработки ошибок и соответственно удобнее это обрабатывать не исключениями, а через Result. vsb>>Нет, удобней это обрабатывать именно исключениями. Т.к. при этом мне не нужно писать никакого кода.
_>Твоя фраза подразумевает, что при обработке с исключениями не надо писать какой-то код, который надо писать при обработке ошибок через Result в Rust'e. И т.к. это очевидно не код самой реакции на ошибку (который естественно надо писать в любом подходе и языке), то тогда о чём собственно речь? Или ты считаешь "дополнительным кодом" несколько знаков вопроса?
Ну смотри. Может я в расте что-то не понимаю.
Во-первых у каждой функции с ошибками надо возвращать Result вместо конкретного типа. Это как минимум синтаксический шум.
Во-вторых если функция сейчас не бросает ошибок, а завтра начинает бросать, мне надо менять её тип, потом проходить по всем местам её использования и как минимум добавлять туда этот самый вопросик. Потенциально надо рекурсивно менять тип этой вызывающий функции и так далее.
В-третьих — да, вопрос это дополнительный код с не такой уж и тривиальной логикой. И один символ тут не должен вводить в заблуждение.
В-четвёртых можно пример, как будет выглядеть код аналогичный
есть у меня подозрение, что вопросиком тут не отделаешься. Конечно на выходе нужен именно результат над списком чисел, а не список результатов над числами.
vsb>>OOM в жаве это отдельная иерархия, но в целом — да, если отвечать на вопрос — пытается. И у него скорей всего получится, т.к. на нижнем уровне там всё используется в пулах преаллоцированных. Хотя, конечно, в условиях OOM всё будет плохо. У меня такой ситуации практически не бывает, но если про это задумываться, я при OOM предпочёл бы просто завершить процесс.
_>Именно! И как раз для таких ситуаций исключения (или паники) подходят идеально.
Насчёт исключений — хз. В жаве есть какой-то флажок, который при первом же OOM-е просто завершает процесс вместо выбрасывания исключения (и вроде хипдамп пишет). Вот это правильный подход и если бы мне не было лень это прописывать, я бы его прописывал.
_>>>А что именно неудобно в работе с Result? vsb>>Ну в го неудобно то, что там под каждым вызовом функции еще 3 строки на обработку err. В расте сахарок сделали, но всё равно везде Result-ы мусорят же. Зачем мне это видеть.
_>Ну тут есть довольно простой ответ, через встречный вопрос. ))) Вот допустим ты используешь механизм исключений для обработки обычных ошибок. Можешь ли ты взяв произвольную функцию в коде точно перечислить список ошибок, который она может вернуть?
Нет, конечно (ну если не считать throws, который был плохой идеей).
Но зачем? Всё, что я знаю — что он может кинуть Exception. Если мне нужно что-то более конкретное — я пойду в нужное место и посмотрю.
В Java придумали throws в своё время который как раз позволяет типы выбрасываемых исключений добавлять в сигнатуру функции. Но это оказалось плохой идеей и сейчас эту фичу стараются избегать.
Здравствуйте, vsb, Вы писали:
vsb>Во-первых у каждой функции с ошибками надо возвращать Result вместо конкретного типа. Это как минимум синтаксический шум.
Вместо T возвращаешь Result<T, MyError>
vsb>Во-вторых если функция сейчас не бросает ошибок, а завтра начинает бросать, мне надо менять её тип, потом проходить по всем местам её использования и как минимум добавлять туда этот самый вопросик.
Неявные точки выхода это зло.
vsb>Потенциально надо рекурсивно менять тип этой вызывающий функции и так далее.
С checked exceptions то же самое. А unchecked это зло.
vsb>
vsb>есть у меня подозрение, что вопросиком тут не отделаешься. Конечно на выходе нужен именно результат над списком чисел, а не список результатов над числами.
Здравствуйте, T4r4sB, Вы писали:
TB>С checked exceptions то же самое. А unchecked это зло.
Вот только checked exceptions никто не использует, включая новый код в стандартной библиотеке (см. UncheckedIOException который оборачивает IOException). От исторического багажа, к сожалению, избавляться пока не собираются. Про популярные библиотеки и фреймворки вообще не говорю, не припомню ни одной, где использовались бы checked exceptions, если такие и есть, то их мало.
checked exceptions, как и result могут быть полезны как один из инструментов для написания своего кода. Очень редкий инструмент, я сколько пишу, так и не нашел, где это могло бы быть полезно. Ну теоретически могу представить функцию, вызывая которую ошибку обработать надо вообще абсолютно всегда, без исключений (простите за каламбур). Иначе там реактор взорваться может. Там это оправдано — заставлять вызывающего функцию обработать ошибку.
Во всех остальных случаях функция не знает, нужно ли вызывающему коду обрабатывать ошибку, которую она может вернуть вместо результата.
Ещё один артефакт проверяемых исключений в стандартной библиотеке Java это класс URI. У него конструктор кидает исключение, если переданная строка — невалидный URI. Но этот класс часто используется в самой Java. Очевидно, программистам стандартной библиотеки настолько надоело это исключение, что они сделали URI.create(str) — который эту ошибку перекидывает через unchecked exception.
Возможно в java стоило бы добавить синтаксис вроде f(x, y)?; который бы прокидывал checked исключение, как unchecked. Ну тут надо попробовать, может бы и получилось удобно, а может быть и нет.
Здравствуйте, vsb, Вы писали:
vsb>checked exceptions, как и result могут быть полезны как один из инструментов для написания своего кода. Очень редкий инструмент, я сколько пишу, так и не нашел, где это могло бы быть полезно. Ну теоретически могу представить функцию, вызывая которую ошибку обработать надо вообще абсолютно всегда, без исключений (простите за каламбур). Иначе там реактор взорваться может. Там это оправдано — заставлять вызывающего функцию обработать ошибку.
Ну вообще во всех местах где пользователю показывают результат работы функции,
неважно в каком виде: веб страница, desktop GUI, stdout нужен не backtrace,
и какое-то невнятное сообщение, а сообщение о том что случилось и как это исправить.
Для это и нужно контролировать прохождение ошибки сверху вниз и добавлять к ней нужный
контекст.
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, Zhendos, Вы писали:
vsb>>checked exceptions, как и result могут быть полезны как один из инструментов для написания своего кода. Очень редкий инструмент, я сколько пишу, так и не нашел, где это могло бы быть полезно. Ну теоретически могу представить функцию, вызывая которую ошибку обработать надо вообще абсолютно всегда, без исключений (простите за каламбур). Иначе там реактор взорваться может. Там это оправдано — заставлять вызывающего функцию обработать ошибку.
Z>Ну вообще во всех местах где пользователю показывают результат работы функции, Z>неважно в каком виде: веб страница, desktop GUI, stdout нужен не backtrace, Z>и какое-то невнятное сообщение, а сообщение о том что случилось и как это исправить. Z>Для это и нужно контролировать прохождение ошибки сверху вниз и добавлять к ней нужный Z>контекст.
И где ты это видел? Везде показывают "Ой, что-то случилось". Да и какое пользователю дело до сообщения? Ну потратишь ты год времени на то, чтобы пользователю написало красивым русским языком — "У нас кончилось место на диске и мы не смогли закоммитить транзакцию" или "Сервис Google Maps вернул ошибку, которая, вероятно, связана с отозванным токеном". И что дальше? Всё, что действительно нужно — это информативная ошибка в логе, по которой девопс сможет как можно быстрей понять причину и устранить или передать разработчикам. И эта информативная ошибка называется стектрейс. Всё, что нужно программисту — иногда добавлять к этому стектрейсу контекстную информацию. Чтобы потом проще было понять, что случилось. URL, какой-нибудь ID объекта, который мы пытались вставить и тд.
Если пользователь что-то может сделать, ну ок, тут надо постараться. Но вообще это плохой UX. Не должно случаться таких ситуаций, чтобы действия пользователя приводили к ошибке, которую он сам может исправить. Если пользователь может, значит может и система исправить. Если вариантов исправления несколько, значит плохо проработан workflow.
Здравствуйте, vsb, Вы писали:
vsb>Во-вторых если функция сейчас не бросает ошибок, а завтра начинает бросать, мне надо менять её тип, потом проходить по всем местам её использования и как минимум добавлять туда этот самый вопросик. Потенциально надо рекурсивно менять тип этой вызывающий функции и так далее.
Это как раз преимущество. Внезапно прилетающее исключение не начнёт ломать код, который к этому не был готов. Мы сразу на этапе компиляции увидим места, которые нужно поправить.
vsb>В-четвёртых можно пример, как будет выглядеть код аналогичный
vsb>
vsb>есть у меня подозрение, что вопросиком тут не отделаешься. Конечно на выходе нужен именно результат над списком чисел, а не список результатов над числами.
Здравствуйте, vsb, Вы писали:
vsb>И где ты это видел? Везде показывают "Ой, что-то случилось". Да и какое пользователю дело до сообщения?
Пользователю, конечно, пофиг. Но явные ошибки всё-таки заставляют задуматься над правильной обработкой: видя список ошибок ты сознательно выбираешь некоторые проигнорировать, какие-то пробросить дальше, из-за каких-то повторить запрос или сделать вообще что-то другое.
Но да, проще писать код не задумываясь над ошибками вообще. А уже по результатам тестирования подпирать костылями try/catch.
Re[11]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
vsb>>Во-вторых если функция сейчас не бросает ошибок, а завтра начинает бросать, мне надо менять её тип, потом проходить по всем местам её использования и как минимум добавлять туда этот самый вопросик. Потенциально надо рекурсивно менять тип этой вызывающий функции и так далее.
DE>Это как раз преимущество. Внезапно прилетающее исключение не начнёт ломать код, который к этому не был готов. Мы сразу на этапе компиляции увидим места, которые нужно поправить.
Я не понимаю, что такое "код, который к этому не был готов". У меня есть воспоминания про exception-safety в С++, но там это было обосновано повсеместным ручным управлением памятью, а в современном C++ наверное и это стало не важно. А в Java что такое код, который не готов к исключениям — ну это прям постараться надо, чтобы такой код написать. Если писать в более-менее общепринятом стиле, где нет долгоживущих объектов, а те, которые долгоживущие — те обычно иммутабельные и из мутабельности там максимум — кеши, где с базой работают через транзакции, то исключение может вылетать в любом месте (вообще-то в Java оно и так может вылетать почти в любом месте) и на корректность системы это не повлияет.
Вот сколько пишу на жаве, лет 15 наверное, не припомню ни одного случая, чтобы исключение в одном запросе как-то повлияло на другой запрос.
Re[15]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
DE>Но да, проще писать код не задумываясь над ошибками вообще. А уже по результатам тестирования подпирать костылями try/catch.
Дык про что я и толкую. А код надо писать так, как проще. А не так, как сложней. Платят не за обработку ошибок, а за функционал. Мало кто будет платить за сервис, который хорошо показывает ошибки, но у которого не хватает нужного функционала.
И я прекрасно понимаю, что это не универсальное правило. Если я пишу консольный клиент для git-а, конечно он должен выдавать хорошие ошибки. Если я пишу компилятор, он должен выдавать просто превосходные ошибки. Это важно. А если я пишу опердень, то этот опердень должен рассчитаться. А всё, что нужно от обработки ошибок — не испортить базу и адекватно вывалить эту ошибку, чтобы я лично с ней разобрался и исправил баг или входные данные или ещё там что-то. Время, потраченное на перфекционирование с ошибками, это скорей всего время, которое могло бы быть потрачено более полезным образом. А код, который потрачен на это перфекционирование с ошибками это код, который нужно будет сопровождать всю оставшуюся жизнь данной софтины.
А ещё могу добавить, что код, который обрабатывает теоретические ошибки, никто никогда не тестирует. Со всеми вытекающими. Вот когда у тебя код изначально максимально простой, потом тебе прилетела проблема, ты эту проблему в юнит-тесте смоделировал и точечно поставил в нужном месте try-catch, вот это идеально. И кода минимум и тестом покрыт.
Здравствуйте, vsb, Вы писали:
vsb>И я прекрасно понимаю, что это не универсальное правило. Если я пишу консольный клиент для git-а, конечно он должен выдавать хорошие ошибки. Если я пишу компилятор, он должен выдавать просто превосходные ошибки. Это важно. А если я пишу опердень, то этот опердень должен рассчитаться.
Никто не говорил, что обработка ошибок в расте через Result — это серебряная пуля. Она где-то удобна, а где-то нет. Вероятно, что для тех целей, для которых изначально создавался раст, Result был лучше других альтернатив. Ну и никто не заставляет использовать раст для опердней (и не только для них), если есть более удобные языки )
Z>>Ну вообще во всех местах где пользователю показывают результат работы функции, Z>>неважно в каком виде: веб страница, desktop GUI, stdout нужен не backtrace, Z>>и какое-то невнятное сообщение, а сообщение о том что случилось и как это исправить. Z>>Для это и нужно контролировать прохождение ошибки сверху вниз и добавлять к ней нужный Z>>контекст.
vsb>И где ты это видел? Везде показывают "Ой, что-то случилось". Да и какое пользователю дело до сообщения? Ну потратишь ты год времени на то, чтобы пользователю написало красивым русским языком — "У нас кончилось место на диске и мы не смогли закоммитить транзакцию" или "Сервис Google Maps вернул ошибку, которая, вероятно, связана с отозванным токеном". И что дальше?
И пользователь исправит эту ошибку.
И большинство софта с которым я работаю в общем-то "популярные" ошибки пользователя так и обрабатываются как я указал в исходном сообщении.
Если пришел код 401/403 по HTTP то почти любое приложение умеет преобразовать его в диалог повторного
запроса логина и пароля, падение процесса/потока отвечающего за вкладку в браузере приводит к диалогу с предложением
перезапустить вкладку, а не только "ой, что-то случилось".
Большинство рабочего софта (IDE, видео редакторы, фото редакторы) показывают ошибку типа кончилось
место и некоторые даже сразу запускают какой-нибудь системный диалог для удаления ненужных файлов.
Re[4]: Почему в расте отсутствует выброс исключений?
Здравствуйте, flаt, Вы писали:
F>Что значит, обнаружил? Это описывается в "The book", которую нужно прочитать в первую неделю знакомства с языком.
F>Как можно что-то использовать, даже не прочитав документацию? Будь-то ЯП или API.
С яп всё как в жизни — сначала учатся говорить, а уже потом идут в школу и изучают язык на уроках.
Re[15]: Почему в расте отсутствует выброс исключений?
Здравствуйте, Zhendos, Вы писали:
Z>И пользователь исправит эту ошибку.
Для этого ему сначала надо взломать сервер (:
Z>И большинство софта с которым я работаю в общем-то "популярные" ошибки пользователя так и обрабатываются как я указал в исходном сообщении. Z>Если пришел код 401/403 по HTTP то почти любое приложение умеет преобразовать его в диалог повторного Z>запроса логина и пароля
Вот это как раз и делается очень удобно с исключениями. Заводится одно исключение UnauthorizedException которое может бросить кто угодно и которое на уровне общего обработчика преобразуется в соответствующий HTTP-код.
Z>Большинство рабочего софта (IDE, видео редакторы, фото редакторы) показывают ошибку типа кончилось Z>место и некоторые даже сразу запускают какой-нибудь системный диалог для удаления ненужных файлов.
Я про серверный софт пишу всё это время.
Re[12]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
vsb>Я не понимаю, что такое "код, который к этому не был готов".
Вот, что у в среднем коде с "неожиданным" исключением произойдёт? Оно где-то на верхнем уровне будет записано в лог и всё. Иногда действительно ничего лучше сделать нельзя, но очень часто можно. Хорошо если мы говорим о "запросах" — тогда проблемный просто будет игнорироваться до того как программист посмотрит логи и пофиксит проблему. С резалтами же не надо ждать баг-репортов от пользователей.
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
vsb>>Я не понимаю, что такое "код, который к этому не был готов".
DE>Вот, что у в среднем коде с "неожиданным" исключением произойдёт? Оно где-то на верхнем уровне будет записано в лог и всё. Иногда действительно ничего лучше сделать нельзя, но очень часто можно. Хорошо если мы говорим о "запросах" — тогда проблемный просто будет игнорироваться до того как программист посмотрит логи и пофиксит проблему. С резалтами же не надо ждать баг-репортов от пользователей.
А зачем их ждать? Настраиваешь алерты по новым эксепшнам и всё. Вылетел эксепшн — пришла смс, открыл лог, посмотрел, пофиксил.
Что тут раст предлагает, я пока тоже, честно говоря, не понимаю.
Ну сделал ты инсерт в базу. Отдала тебе базу ошибку какую-то. Код такой-то, текст ошибки такой-то. И что дальше? Кодов ошибок в БД тысячи. Причин, по которым не сработал инсерт — тоже много. В итоге так же отдашь 500 и всё. Или панику кинешь. А когда в эксплуатации увидишь, что там конкретная ошибка срабатывает вроде нарушения уникальности, то пропишешь по коду сравнение и обработку, ровно так же, как будет в жаве.
Здравствуйте, vsb, Вы писали:
vsb>И я прекрасно понимаю, что это не универсальное правило.
Это хорошо, тогда у нас нет принципиальных разногласий.
vsb>Если я пишу консольный клиент для git-а, конечно он должен выдавать хорошие ошибки. Если я пишу компилятор, он должен выдавать просто превосходные ошибки.
Но ты почему-то зациклен на "показать ошибку". Все эти заморочки с резалтами они не для того, чтобы раньше, лучше и точнее написать в лог или показать пользователю проблему. Они для того, чтобы ты при написании и чтении кода мог увидеть потенциально проблемные моменты.
Ну и наконец есть штуки вроде anyhow. Я бы сказал, что с ними скорость написания кода практически не отличается от аналога с исключениями. Да, в тексте программы будет чуть больше "шума". И вот именно тут мы и расходимся во мнении. Я не считаю это шумом, а наоборот полезной информацией.
Можно ещё поспорить насчёт того, что на самом деле будет быстрее: один раз посмотреть на возможные ошибки, которые возвращает функциям, и подумать, что с ними делать. Или написать код, проверить его вручную или тестами на ограниченных сценариях, добавить обработку ошибок при необходимости, отдать на тестирование, опять добавить try/catch и т.д. Но объективных метрик у меня, ясное дело, нет. В целом лично мне нравится получать максимум информации из типов, а не полагаться на документацию, которая может быть не идеальной или даже отсутствовать. А ещё документация имеет свойство устаревать.
Ну и снова согласен с тобой: если обработка ошибок в 99% случаев сводится к "игнорируем проблему и пишем на верхнем уровне в лог", то да, можно не заморачиваться.
Re[14]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
vsb>А зачем их ждать? Настраиваешь алерты по новым эксепшнам и всё. Вылетел эксепшн — пришла смс, открыл лог, посмотрел, пофиксил. vsb>Что тут раст предлагает, я пока тоже, честно говоря, не понимаю.
В идеале, "пофиксить" можно при написании кода, не дожидаясь уведомлений о новых проблемаx после того как новая версия была залита на прод. Понятное дело, это не всегда будет работать, особенно если если брать крайние случаи когда ошибка — это "случалась непонятная хрень".
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления.
Потому что язык молодой, зубы пока молочные. Коренные появятся позже.
Оно так было с большинством языков — исключения появились через N итераций. Исключения (хехе, каламбурчик) были (кажется, в Java с самого начала), но в большинстве случаев исключения появлялилсь много позже, при росте сложности проектов и все более частой необходимости non-local return с возможностью посмотреть исходный call stack. Это не более чем формализация практики, когда error return оборачивают в tuple типа {error, Reason, Location} на каждом уровне из цепочки вызовов (т.н. "bubble wrapping"), по сути создавая call stack врукопашную. Так что и в Rust оно появится. Просто позже.
Re[2]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
vsb>Для этого ему сначала надо взломать сервер (:
Для этого не нужно взламывать сервер, конечно пользователю показываются
ошибки которые он может исправить: ввести логин и пароль, загрузить другой файл,
предоставить правильную ссылку и так далее.
Z>>И большинство софта с которым я работаю в общем-то "популярные" ошибки пользователя так и обрабатываются как я указал в исходном сообщении. Z>>Если пришел код 401/403 по HTTP то почти любое приложение умеет преобразовать его в диалог повторного Z>>запроса логина и пароля
vsb>Вот это как раз и делается очень удобно с исключениями. Заводится одно исключение UnauthorizedException которое может бросить кто угодно и которое на уровне общего обработчика преобразуется в соответствующий HTTP-код.
И в результате будет сделано как раз то, что вы говорили что никто не делает: Z>а сообщение о том что случилось и как это исправить. Z>Для это и нужно контролировать прохождение ошибки сверху вниз и добавлять к ней нужный Z>контекст.
Мы перехватываем ошибки/коды типа 401,403 плюс ошибки связанные со сроком действия токена
и бросаем вместо него UnauthorizedException
Z>>Большинство рабочего софта (IDE, видео редакторы, фото редакторы) показывают ошибку типа кончилось Z>>место и некоторые даже сразу запускают какой-нибудь системный диалог для удаления ненужных файлов.
vsb>Я про серверный софт пишу всё это время.
А какая разница? Так или иначе весь серверный код взаимодействует пользователю,
из-за того что условный Photoshop открывается с помощью загрузки сайта, а не через запуск exe что-то измениться?
Мне кажется как раз наоборот, если раньше при ошибке о недостатка памяти можно было просто упасть,
теперь лучше сообщить об этом пользователю, ведь это реальные деньги: "памяти не хватает для обратки операции XYZ,
оплатите переходите на тариф "ПРО" за +100$ и объем доступной памяти будет увеличен вдвое".
Re[2]: Почему в расте отсутствует выброс исключений?
Здравствуйте, SkyDance, Вы писали:
SD>Оно так было с большинством языков — исключения появились через N итераций.
Не уверен, что это правда, особенно если брать что-то посвежее джавы. По моему исключения в множестве языков были изначально. А уже если на совсем свежие посмотреть, такие как Swift, Go и Rust, то что-то пока исключений не видно. И ладно раст, про него ещё можно сказать, что раз не мейнстрим, то семь лет — это ещё мало, но Go и Swift вполне активно используются. И что-то пока исключений не завезли.
Кстати, в Swift, как по мне, худшее из двух миров: у них там ошибки называются исключениями, но по факту это именно аналог растовых резалтов. В итоге и код зашумляется try, которые надо писать перед каждой функцией, которая можно возвращать ошибку, и в сигнатурах не понятно, что именно за ошибки.
Re[17]: Почему в расте отсутствует выброс исключений?
Здравствуйте, Zhendos, Вы писали:
Z>Для этого не нужно взламывать сервер, конечно пользователю показываются Z>ошибки которые он может исправить: ввести логин и пароль, загрузить другой файл, Z>предоставить правильную ссылку и так далее.
Такие ошибки вообще не должны кидать исключения ни на каком этапе. Неправильный логин — селект возвращает null (или optional для эстетов), код простым if-ов разбирается с ситуацией. То же с паролем и тд. Конечно иногда API нужного просто нет, например в жаве нельзя распарсить строку в число без обработки исключения, но это уже недостаток API. Ряд API было бы неплохо дублировать для двух юз-кейсов. К примеру я в своих программах при работе с базой различаю методы find, которые всегда возвращают один результат, а если результатов 0 или 2+, то кидают исключение и методы query, которые возвращаю Optional. В итоге в коде я либо использую метод find без каких-то дополнительных проверок (к примеру если в URL-е пришёл ID, зачем его проверять, URL это не поддерживаемый элемент интерфейса и ввод кривого URL-а просто приведёт к выбросу этого исключения и возврату его в итоге как 404 страницы), либо метод query, если это уже нормальная форма поиска, к примеру.
Z>А какая разница? Так или иначе весь серверный код взаимодействует пользователю, Z>из-за того что условный Photoshop открывается с помощью загрузки сайта, а не через запуск exe что-то измениться? Z>Мне кажется как раз наоборот, если раньше при ошибке о недостатка памяти можно было просто упасть, Z>теперь лучше сообщить об этом пользователю, ведь это реальные деньги: "памяти не хватает для обратки операции XYZ, Z>оплатите переходите на тариф "ПРО" за +100$ и объем доступной памяти будет увеличен вдвое".
Разница в том, что пользователь в случае с серверным софтом не может ничего исправить. Ну про тарифы про не знаю, с таким не сталкивался. В любом случае это очень точечные обработки ошибок, ради которых по всему коду их обрабатывать — избыточно.
Re[3]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
DE>Не уверен, что это правда, особенно если брать что-то посвежее джавы. По моему исключения в множестве языков были изначально. А уже если на совсем свежие посмотреть, такие как Swift, Go и Rust, то что-то пока исключений не видно. И ладно раст, про него ещё можно сказать, что раз не мейнстрим, то семь лет — это ещё мало, но Go и Swift вполне активно используются. И что-то пока исключений не завезли.
Swift это про совместимость с кучей Objective C кода и API, которые никто никогда переписывать не будет.
Go — ну вот исключения это единственное, чего мне не хватает, чтобы объявить его идеальным вариантом для меня.
Rust — системный язык, я полагаю, что исключения для системного языка это спорная технология.
Проблема создания языка очень высокого уровня с GC и подобным в том, что питон и C# уже существуют. Всё остальное — спектрум между ними и не совсем понятно, что тут можно вообще придумать.
Лично мне хотелось бы видеть Rust-подобный язык с исключениями, GC и зелёными потоками. Без ownership-а. Мне в Rust нравится очень много. Но бэкэнд я на нём писать не буду. Я в курсе, что ownership используется не только для памяти, но и для потокобезопасного кода и тд, но мне это никогда не было нужно, поэтому я не слишком убеждён.
А вообще, честно говоря, мне надо просто написать свой фреймворк для Java и успокоиться ) По-сути мне в Java не нравятся только жирнющие фреймворки и небольшие недостатки языка, которые можно потерпеть.
Re[4]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
vsb>Swift это про совместимость с кучей Objective C кода и API, которые никто никогда переписывать не будет.
B Objective-C исключения вполне есть.
vsb>Проблема создания языка очень высокого уровня с GC и подобным в том, что питон и C# уже существуют. Всё остальное — спектрум между ними и не совсем понятно, что тут можно вообще придумать.
Ну так-то когда придумывали C#, то джава уже существовала, но языки продолжают появляться. Вон Kotlin вполне себе взлетел. В вебе тайпскрипт, вроде как, стал весьма популярен. А вообще всякое интересное регулярно придумывают, правда стать мейнстримом у этого шансов весьма мало.
vsb>Лично мне хотелось бы видеть Rust-подобный язык с исключениями, GC и зелёными потоками. Без ownership-а.
Так что тогда останется от языка? По моему всё остальное уже в том или ином виде уже где-то есть.
Re[5]: Почему в расте отсутствует выброс исключений?
Здравствуйте, DarkEld3r, Вы писали:
vsb>>Swift это про совместимость с кучей Objective C кода и API, которые никто никогда переписывать не будет.
DE>B Objective-C исключения вполне есть.
В эппловых фреймворках везде используется возврат ошибки через NSError**. Я может ошибаюсь, но я не припомню ни одного места, где я бы использовал исключения в Objective C.
vsb>>Проблема создания языка очень высокого уровня с GC и подобным в том, что питон и C# уже существуют. Всё остальное — спектрум между ними и не совсем понятно, что тут можно вообще придумать.
DE>Ну так-то когда придумывали C#, то джава уже существовала, но языки продолжают появляться. Вон Kotlin вполне себе взлетел. В вебе тайпскрипт, вроде как, стал весьма популярен. А вообще всякое интересное регулярно придумывают, правда стать мейнстримом у этого шансов весьма мало.
Смысл в том, что питон это как некий нижний деноминатор для новичков и C# как язык, где есть вообще всё на свете. Kotlin придумывали, когда Java загнивала. С тех пор на бэкэнде у котлина, имхо, будущего нет. Ну гугл на андроид его протащила, да, по каким-то своим причинам. Пока андроид существует, с котлином нормально всё будет.
Тайпскрипт тоже паразитирует на JS. Самостоятельности у него нет.
Да, интересное придумывают, но оно — такое, пытается заполнить какие-то ниши, а не полноценная платформа общего назначения вроде того же .NET. Из последнего только Go, наверное. Ну он, конечно, уникальный.
vsb>>Лично мне хотелось бы видеть Rust-подобный язык с исключениями, GC и зелёными потоками. Без ownership-а.
DE>Так что тогда останется от языка? По моему всё остальное уже в том или ином виде уже где-то есть.
В языке куча приятных конструкций. Банально синтаксис мне нравится. Шаблоны, ООП без классов, макросы хорошие. Ну да, в том или ином виде есть, а так, чтобы было всё приятно — я не видел. Везде какие-то нагромождения лигаси или спорных решений. Да банально snake_case. Уж извините, но мне он нравится, как бы глупо это ни звучало. Я же не буду на Java писать так, меня не поймут. В расте мне всё нравится, кроме ownership (последнее — не то, чтобы не нравилось, идея-то крутая, просто мне оно не надо, GC удобней, рассуждения примерно того же уровня, что и исключения против возврата ошибок).
Re[10]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
_>>Твоя фраза подразумевает, что при обработке с исключениями не надо писать какой-то код, который надо писать при обработке ошибок через Result в Rust'e. И т.к. это очевидно не код самой реакции на ошибку (который естественно надо писать в любом подходе и языке), то тогда о чём собственно речь? Или ты считаешь "дополнительным кодом" несколько знаков вопроса? vsb>Ну смотри. Может я в расте что-то не понимаю. vsb>Во-первых у каждой функции с ошибками надо возвращать Result вместо конкретного типа. Это как минимум синтаксический шум.
Это ясное выражение в коде того факта, что функция может сгенерировать ошибку. Ведь далеко не все функции такие, есть множество с гарантированным выполнением. Хотя в языке с исключениями об этом факте сложно судить.
vsb>Во-вторых если функция сейчас не бросает ошибок, а завтра начинает бросать, мне надо менять её тип, потом проходить по всем местам её использования и как минимум добавлять туда этот самый вопросик. Потенциально надо рекурсивно менять тип этой вызывающий функции и так далее.
Это как раз максимально удобно, потому что тут тебе IDE сама покажет место, куда необходимо вставить обработчик ошибок. Ведь для ошибок (а не критических ситуаций, которые обрабатываются паниками) как раз характерна обработка по месту, а не один глобальный обработчик на всё приложение.
vsb>В-третьих — да, вопрос это дополнительный код с не такой уж и тривиальной логикой. И один символ тут не должен вводить в заблуждение.
Если ты имеешь в виду ассемблер, то как бы он и в языках с исключениями имеется (слышал про про формирование фрейма исключений?), просто его вставляет компилятор автоматически. И кстати это одна из известных проблем оптимизации, в том смысле что в большинстве языков нет способа сказать компилятору что данная функция точно ничего не бросает (и соответственно тут можно многое оптимизировать). В C++ для этого даже пришлось вводить отдельное ключевое слово! )
vsb>В-четвёртых можно пример, как будет выглядеть код аналогичный vsb>
vsb>есть у меня подозрение, что вопросиком тут не отделаешься. Конечно на выходе нужен именно результат над списком чисел, а не список результатов над числами.
На это тебе уже тут подробно ответили. )
_>>Именно! И как раз для таких ситуаций исключения (или паники) подходят идеально. vsb>Насчёт исключений — хз. В жаве есть какой-то флажок, который при первом же OOM-е просто завершает процесс вместо выбрасывания исключения (и вроде хипдамп пишет). Вот это правильный подход и если бы мне не было лень это прописывать, я бы его прописывал.
Это и есть образцовая паника. )))
_>>Ну тут есть довольно простой ответ, через встречный вопрос. ))) Вот допустим ты используешь механизм исключений для обработки обычных ошибок. Можешь ли ты взяв произвольную функцию в коде точно перечислить список ошибок, который она может вернуть? vsb>Нет, конечно (ну если не считать throws, который был плохой идеей). vsb>Но зачем? Всё, что я знаю — что он может кинуть Exception. Если мне нужно что-то более конкретное — я пойду в нужное место и посмотрю.
В какое такое нужное место? В документацию на функцию из библиотеки? Ну допустим там могут (и то далеко не всегда такое можно увидеть в описание функции) перечислить свои родные исключения. А они точно перечислят ещё и все возможные исключения чужого кода (включая стандартную библиотеку), вызовы которого есть внутри вызова их функции?
vsb>В Java придумали throws в своё время который как раз позволяет типы выбрасываемых исключений добавлять в сигнатуру функции. Но это оказалось плохой идеей и сейчас эту фичу стараются избегать.
Ну вот ты можешь рассматривать сигнатуру функции с Result в Rust, как функцию в Java с использование throws. Но есть два отличающихся нюанса, которые полностью меняют всю картину:
1. В Java данная фича является опциональной, а не обязательной что на корню режет идею о любых гарантиях. Т.е. в таком виде это действительно бесполезное действо, только отнимающее время.
2. В Java нет требования сводить все исключения, бросаемые функцией, к одному типу (пусть даже и максимально абстрактному базовому типу), так что в итоге оператор throws мог выглядеть ужасно (огромным списком из разных иерархий). В Rust'е же это всегда один тип (хотя это может быть и спец. контейнер, который может хранить произвольные типы других ошибок), так что всегда имеет лаконичный код и удобную обработку (через сопоставление с образцом).
Re[16]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
Z>>И большинство софта с которым я работаю в общем-то "популярные" ошибки пользователя так и обрабатываются как я указал в исходном сообщении. Z>>Если пришел код 401/403 по HTTP то почти любое приложение умеет преобразовать его в диалог повторного Z>>запроса логина и пароля vsb>Вот это как раз и делается очень удобно с исключениями. Заводится одно исключение UnauthorizedException которое может бросить кто угодно и которое на уровне общего обработчика преобразуется в соответствующий HTTP-код.
Интересно чем этот текст отличается от:
Заводится один тип ошибки UnauthorizedException, который может вернуть кто угодно и который на уровне общего обработчика преобразуется в соответствующий HTTP-код?
Т.е. чем по твоему "throw UnauthorizedException()" удобнее чем "return Err(UnauthorizedException())"?
Re[18]: Почему в расте отсутствует выброс исключений?
Здравствуйте, vsb, Вы писали:
Z>>Для этого не нужно взламывать сервер, конечно пользователю показываются Z>>ошибки которые он может исправить: ввести логин и пароль, загрузить другой файл, Z>>предоставить правильную ссылку и так далее. vsb>Такие ошибки вообще не должны кидать исключения ни на каком этапе. Неправильный логин — селект возвращает null (или optional для эстетов), код простым if-ов разбирается с ситуацией. То же с паролем и тд. Конечно иногда API нужного просто нет, например в жаве нельзя распарсить строку в число без обработки исключения, но это уже недостаток API. Ряд API было бы неплохо дублировать для двух юз-кейсов. К примеру я в своих программах при работе с базой различаю методы find, которые всегда возвращают один результат, а если результатов 0 или 2+, то кидают исключение и методы query, которые возвращаю Optional. В итоге в коде я либо использую метод find без каких-то дополнительных проверок (к примеру если в URL-е пришёл ID, зачем его проверять, URL это не поддерживаемый элемент интерфейса и ввод кривого URL-а просто приведёт к выбросу этого исключения и возврату его в итоге как 404 страницы), либо метод query, если это уже нормальная форма поиска, к примеру.
Интересно, что по ходу дискуссии ты стал высказывать тезисы на 100% совпадающие с моим изначальным посылом. Хотя в начале вроде как спорил с ним...
Re[3]: Почему в расте отсутствует выброс исключений?
DE> По моему исключения в множестве языков были изначально.
У меня противоположные ощущения, почти всегда (кроме Java) они были добавлены на том или ином этапе. Обычно через десяток лет. Совсем уж юным языкам, тем же Swift/Go, пока только предстоит. Возможно, конечно, там предусмотрят что-то, называющееся иначе. Но с теми же свойствами: non-local return с автоматическим сбором данных по пути от места выброса до места обработки.
Re[11]: Почему в расте отсутствует выброс исключений?
_>Это как раз максимально удобно, потому что тут тебе IDE сама покажет место, куда необходимо вставить обработчик ошибок. Ведь для ошибок (а не критических ситуаций, которые обрабатываются паниками) как раз характерна обработка по месту, а не один глобальный обработчик на всё приложение.
Вообще-то, в большинстве случаев ошибки обработать можно не "по месту" (где функция вызвана), а где-то десятком уровней выше по стеку. Для этого и нужны исключения, чтобы иметь возможность обработать ошибку не там, где она возникла, а там, где имеется достаточно информации для ее обработки.
Как правило, непосредственный caller не имеет эту информацию, поэтому тоже вынужден вернуть ошибку, и далее по курсу (см. "bubble wrapping", видимо, мне пора уже начать публиковать все внутренние статьи, что я писал за годы, и объяснял, что bubble wrapping в итоге почти всегда превращается в самодельные exceptions).
Re[4]: Почему в расте отсутствует выброс исключений?
Здравствуйте, SkyDance, Вы писали:
SD>У меня противоположные ощущения, почти всегда (кроме Java) они были добавлены на том или ином этапе.
Хаскелисты наоборот до сих пор жалеют что с дуру добавили исключения, но выпилить их уже не могут.
В раст просто унаследовали хаскелевское заболевание головного мозга что ошибки всегда должны выражаться в типах
Здравствуйте, SkyDance, Вы писали:
SD>У меня противоположные ощущения, почти всегда (кроме Java) они были добавлены на том или ином этапе. Обычно через десяток лет.
В C# 1.0 разве не было исключений?.. В питоне исключения были ещё до версии 1.0, если я правильно понимаю. В Ruby, вроде, тоже, как и в Objective-C. Впрочем, не со всеми из этих языков я близко знаком и где-то мог ошибиться, но быстрое гугление подтверждает.
Даже с С++ не всё так просто. Да, формально от первого упоминания языка до появления исключений прошло дофига времени, но так-то в первом стандарте они уже были. Кстати, мне кажется, что у новых языков такой фокус не пройдёт: если у языка нет какой-то "киллер фичи", ну или сильной поддержки от корпорации (в идеале монополиста на платформе), то выкатить что-то сырое, а потом десять лет добавлять базовые вещи не получится.
SD>Совсем уж юным языкам, тем же Swift/Go, пока только предстоит.
Поживём увидим.
Re[5]: Почему в расте отсутствует выброс исключений?
FR>Хаскелисты наоборот до сих пор жалеют что с дуру добавили исключения, но выпилить их уже не могут.
Хехе
Вариантов-то всего два, либо есть checked exception(s), либо нет. Если они есть, приходится везде explicitly добавлять что-то вроде ? (Rust), maybe (Erlang), with (Elixir) и прочие варианты.
Если нет, то "что-то может быть где-то implicitly, и этого не будет видно". И так плохо, и этак. Так что варианта по сути два: либо добавлять exceptions в язык, либо делать их поверх (путем создания всяких там Result<Success,Error>, {ok, Result} | {error, Reason}, и т.п.).
Лично мне нравятся exceptions, просто потому что кода меньше. Но я также понимаю тех, кто хотят explicit mention что функция может стрельнуть. Оно удобно, особенно в хорошо изолированных проектах. Но когда попадаешь в гигантский монолит из легаси-спагетти, еще и работающего на распределенном кластере, там практически каждый вызов имеет этот maybe/with/?.
Re[12]: Почему в расте отсутствует выброс исключений?
Здравствуйте, SkyDance, Вы писали:
_>>Это как раз максимально удобно, потому что тут тебе IDE сама покажет место, куда необходимо вставить обработчик ошибок. Ведь для ошибок (а не критических ситуаций, которые обрабатываются паниками) как раз характерна обработка по месту, а не один глобальный обработчик на всё приложение. SD>Вообще-то, в большинстве случаев ошибки обработать можно не "по месту" (где функция вызвана), а где-то десятком уровней выше по стеку. Для этого и нужны исключения, чтобы иметь возможность обработать ошибку не там, где она возникла, а там, где имеется достаточно информации для ее обработки.
Конечная обработка (в смысле показа сообщения пользователю) действительно обычно происходит где-то на несколько уровней выше точки возникновения ошибки. Но нюанс в том, что для нормальной обработки (показа информативного сообщения и т.п.) необходимо собрать контекстную информацию на почти каждом промежуточном уровне. Т.е. по сути если делаешь на исключениях, то надо их ловить вокруг каждого вызова функции и далее выбрасывать новое (с добавленной информацией этого уровня). Именно это я имел в виду, когда говорил о необходимости обработки по месту.
В Rust'е в принципе всё тоже самое, но есть пара нюансов. Во-первых в нём невозможно пропустить эту самую обработку (если достаточно перенаправить ошибку выше без всяких контекстов, то просто ставишь оператор "знак вопроса" в этой точке, но при этом ты явно видишь что здесь возможно возникновение ошибки и все возможные её типы). А во-вторых развиты средства её автоматизации (выглядит это например так https://docs.rs/anyhow/latest/anyhow/).
Re[13]: Почему в расте отсутствует выброс исключений?
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, SkyDance, Вы писали:
_>В Rust'е в принципе всё тоже самое, но есть пара нюансов. Во-первых в нём невозможно пропустить эту самую обработку (если достаточно перенаправить ошибку выше без всяких контекстов, то просто ставишь оператор "знак вопроса" в этой точке, но при этом ты явно видишь что здесь возможно возникновение ошибки и все возможные её типы). А во-вторых развиты средства её автоматизации (выглядит это например так https://docs.rs/anyhow/latest/anyhow/).
Думаю одно из самых важных средств при проталкивании ошибки вверх это context(), добавляя описания что собственно собирались сделать. И вместо "error opening file" где то сверху ты получаешь что то типа:
Error: Failed to read instrs from ./path/to/instrs.json
Caused by:
No such file or directory (os error 2)
Re[13]: Почему в расте отсутствует выброс исключений?
_>В Rust'е в принципе всё тоже самое, но есть пара нюансов. Во-первых в нём невозможно пропустить эту самую обработку (если достаточно перенаправить ошибку выше без всяких контекстов, то просто ставишь оператор "знак вопроса" в этой точке, но при этом ты явно видишь что здесь возможно возникновение ошибки и все возможные её типы)
В большинстве случаев нужен как раз тот самый "знак вопроса". Поэтому многие языки именно его и имеют по умолчанию. Дабы снизить количество boilerplate. И выражать только конкретное намерение (intent) добавить некий контекст к пролетающему исключению. Там где нечего добавить — ничего и писать не надо. Потому этот знак вопроса лишний.
vaa>Почему в расте отсутствует выброс исключений? Это же удобный способ передачи управления.
Очень хорошая статья по ссылкам.
I suspect that most members of the C++ community vastly underestimate the skills needed to program with exceptions and therefore underestimate the true costs of their use. The popular belief is that exceptions provide a straightforward mechanism for adding reliable error handling to our programs. On the contrary, I see exceptions as a mechanism that may cause more ills than it cures. Without extraordinary care, the addition of exceptions to most software is likely to diminish overall reliability and impede the software development process.
This “extraordinary care” demanded by exceptions originates in the subtle interactions among language features that can arise in exception handling. Counter-intuitively, the hard part of coding exceptions is not the explicit throws and catches. The really hard part of using exceptions is to write all the intervening code in such a way that an arbitrary exception can propagate from its throw site to its handler, arriving safely and without damaging other parts of the program along the way.