Сложить два числа - не странное. Сложить две строки - странное.
Тот факт, что интерфейс работы с числовой арифметикой не идентичен у Rust и C++ не является проблемой Rust или C++. Здесь только ваши заблуждения.
Тоже самое и в контексте перегрузки операторов. Семантика и практика применения другая. Вы упорно утверждаете, что оно обязано быть идентичным.
Многие инструменты при использовании приносят не только пользу, но и вред. Так у механизма исключений есть проблемы по части производительности и переносимости. Нецелевое использование перегрузки операторов порождает код, работа которого неочевидна. Тоже самое и для некоторых других штук.
Rust пытается своим дизайном предотвратить часть неправильного использования подобных инструментов. Может не всегда, но достаточно часто у него это получается. В конце концов для самых упоротых упорных есть инструменты для воплощения своих фантазий. Использование подобного будет сложнее использования более подходящего инструмента. И это сделано намерено.
unsafe означает, что вы обязаны проверить все инварианты вручную. Полностью внутри блока unsafe. Всё, что покидает блок unsafe, обязано соответствовать save-гарантиям.
Колбасу принято пихать в рот. Более того, на колбасе написано, что её в рот. Если вы, как разработчик, привыкли пихать колбасу в непредназначенные для этого отверстия, то чтож, вы всё ещё можете продолжать делать это. Кто я такой, чтоб осуждать вас. Но прошу вас, пожалуйста, не делайте этого на людях. И уж точно не стоит обвинять колбасу в том, что её диаметр не соответствует вашим ожиданиям. Тут я могу посоветовать только продолжать разработку.
Да в большинстве случаев это необходимо
Лишнее копирование на стек удаляется компилятором. Я не поленился и сходил проверить простенький случай. Потому и прошу вас привести пример, когда это не работает.
Ну и по какой же причине эту возможность так до сих пор и не реализовали, не задумывались?
Поверхностный поиск показывает, что корректнее говорить не о том, что не реализовали, а о том, что не стабилизировали. Увы, детали пояснить не могу. Не являюсь тем, кто реализует эту фичу. Раз вы знаете больше, так объясните.
Ну и по какой же причине эту возможность так до сих пор и не реализовали, не задумывались?
Не задумывался. Но теперь мне интересно и я готов послушать. Могу лишь предположить, что фича достаточно сложная, с нюансами.
Начинаю беспокоиться о вашем ментальном здоровье. Старайтесь соблюдать рекомендации психиатра. Я свои таблетки принял и рекомендую вам сделать тоже самое. Иначе опять санитар стучаться в палату будет.
как в рамках раста обработать ошибки типа знакового переполнения или целочисленного деления на 0 при использовании математических операторов "для штук, подобных математическим примитивам"
В зависимости от контекста. В большинстве случаев писать нормальный код, который заранее валидирует входные данные. Компилятор скорее всего оценит ваши старания и хорошенько соптимизирует. Есть всякие checked_div/overflowing_div/saturating_div для некоторых специальных случаев.
Если вы привыкли использовать перегрузку операторов для странного, то это это плохая привычка.
Ну да, как я и сказал. "Потребности в колбасе сегодня нет" (С) древний анекдот.
Выше ответил. В Rust перегрузка операторов используется сильно реже и только в тех местах, где это необходимо. Скажем, складывание строк выглядит достаточно глупо.
Естественно, без необходимости оборачивания этого в unsafe, ведь это нибизапастно.
Вы используете unsafe тогда и только тогда, когда компилятор не имеет возможности доказать что-то. unsafe fn или там unsafe trait означает, что помимо гарантий, предоставляемых Rust, вы ещё должны помнить о дополнительных ограничениях. Использование unsafe {} означает, что вы удовлетворяете эти ограничения самостоятельно. Но ничего сверх этого. Механика unsafe не является инструментом оптимизаций или способом хакнуть систему. Только тем, что написано на этикетке.
Т.е. не создавать сначала объект на куче/стеке, а потом копировать или перемещать его в Vec, а создавать сразу там на месте, в неинициализированном куске памяти, предвыделенном Vec.
Приведите пример, когда это необходимо. В большинстве случаев всё равно нужно некое инициализирующее значение и его в любом случае придётся перемещать откуда-то. Часть простых случаев скорее всего итак оптимизируется. Для другой части есть MaybeUninit (включая инициализацию нулями).
Как думаете, почему такой возможности до сих пор нет?
По той причине, что эту возможность ещё не реализовали.
потерю производительности, потому что теперь ветвление всегда
Обработка исключений может быть гораздо дороже. На некоторых архитектурах настолько, что вообще нет возможности их использовать. Опять же куча оптимизаций есть, которые могут превратить бранч во что-то условно бесплатное. В современных архитектурах есть branch prediction, а в современных компиляторах LTO, PGO и прочие ништяки.
потерю читаемости кода, потому что unwrap или match теперь просто везде
Да не, просто один оператор ?. Алсо чем match вас смущает в сравнении с try-catch? Полагаю, тем, что match это гораздо более мощный и гораздо более удобный инструмент.
ухудшение интерфейса
Тем, что всегда требуем явно писать возможные ошибки? Так это наоборот улучшение. Явное лучше неявного.
потеря copy elision
Бессмыслено натягивать эти термины на Rust с его move-семантикой. Лучше сходите и напишите ещё один копирующий конструктор.
как я могу реализовать оператор+ для строк, ведь может не хватить памяти и кинется исключение?
Никак. Математические операторы предназначены для штук, подобных математическим примитивам. Строки математическими примитивами не являются. Об этом всём сказано в документации. Для хитрой конкатерации строк есть макрос format_args! и куча разных штук поверх него.
Я не говорю о том, что unsafe применять нельзя вообще. Это очень важный и очень сложный инструмент. Но нужен подобный инструментарий редко и требует дополнительной квалификации. Тоже самое в разной степени касается, например, перегрузки операторов и процедурных макросов (впрочем и обычных макросов это тоже касается). Почти всегда вы избегаете использования подобных инструментов. Кроме тех случаев, когда без них не обойтись. Это как наклейка "осторожно, высокое напряжение" на трансформаторной будке. Перед тем, как полезть внутрь, убедитесь что вы электрик.
Немного другой пример. Трейты являются практически полноценной реализацией ООП со всеми стандартными наворотами включая множественное наследование. При этом Rust определённо не является ООП языком.
И в целом без исключений трудно нормально обрабатывать к примеру ошибки в реализациях перегруженных операторов - разве что и в них обмазываться Option или Result.
Нет. Вместо этого вы делаете метод, который возвращает Option/Result. Я всё понимаю, микроскоп красивый, новенький, блестящий. Однако всё же не стоит забивать им гвозди.
Вообще это довольно важная часть дизайна на мой взгляд. Rust старается оградить вас от неправильного использования собственного инструментария. В некотором смысле это также реализация принципа "не плати за то, что не используешь", но в плане когнитивной сложности.
Вместо того, чтоб писать какие-то хитрые штуки с возвратом ошибок из перегруженный операторов вы просто паникуете там + пишете методы. А оператор может быть тупо вызовом метода + unwrap. Ну или не перегружать ничего. И вам не нужно держать в голове особенности поведения конкретной реализации определённого оператора. Вместо этого вы можете один раз запомнить общий случай.
Паники отличаются от исключений тем, что единственный адекватный способ их "обработать" - написать корректный код. При схожести механизма работы, парадигма совершенно другая. Но у вас всё ещё есть возможность их ловить в очень специфических случаях.
Кстати напомните, почему в расте до сих пор нет адекватного аналога плюсовому placement new? Не потому ли, в том числе, что нет механизма дать вызывающему коду понять, был ли все-таки сконструирован объект или нет?
Понятия не имею если честно. Не изучал этот вопрос. Буду рад, если вы приведёте реалистичный пример, где это вообще может быть нужно, std::mem::MaybeUninit недостаточно, а другими способами не реализуемо.
Есть что-то забавное в том, что в последнем примере всё решилось удалением unsafe-кода. Соблюдать правило "не писать unsafe без крайней необходимости" проще, чем следить за UB в C++ коде. Намного проще. На порядки.
Опять же важно не наличие подобных ошибок и даже не их количество, а простота их обнаружения и исправления. Проверка условного десятка строк с пометкой unsafe сильно дешевле полного аудита всего кода.
И кстати, вы не совсем правы на счёт того, что паники нельзя отловить. Всё немножечко сложнее, чем просто можно/нельзя. За подробностями рекомендую читать документацию. Ну и замечу, что не смотря сильное сходство, паники не являются аналогом исключений.
Прочитайте внимательнее отчёт АНБ. Они говорят о том, что вот же куча разных ЯП, оставьте няшную сишечку и плюсы для специальных случаев.
А насчёт универсальности - она работает, но только тогда, когда имеет очевидное превосходство. Опять же USB вполне себе стал стандартом, как контрпример про разъёмы. Английский язык в некоторых областях тоже что-то вроде негласного стандарта (например в научном мире). Система СИ так или иначе используется везде в технической/научной среде. Во всех этих случаях оно работает только в областях, где есть какие-то причины для этого.
Скажем, нет какой-то особой разницы между дюймами, миллиметрах или бананами в дисплеях. Когда вы их продаёте. А вот когда вы производите дисплеи, то там будут использоваться единицы СИ.
Ну а что rust ... судя по использованию, ничего выдающегося там не водится; это всё попытки переплюнуть C++ ... упс.
Полагаю, это уже лично мне претензия. Не очень понимаю, с чего вы взяли, что Rust вообще в принципе предлагает что-то выдающееся. С моей точки зрения это хорошо сделанный инструмент. Весьма неплохо себя показывает, если вам нужно достичь надёжности и производительности одновременно, но вы готовы заплатить сложностью. Опять же как и у любого другого ЯП, у Rust есть области, для которых оный подходит плохо. Например для приложений, где нативный GUI является очень важной частью, я Rust крайне не рекомендую.
Полагаю, я всё же должен уточнить о вашем опыте использования Rust. Здесь есть относительно высокий порог вхождения. Как минимум прочитать основную документацию (включая оффициальные учебные пособия) и написать некоторое количество кода. Впрочем это характерно и для многих других ЯП, которые содержат какие-то новые для вас парадигмы. Borrow checker не смотря на кажущуюся простоту концепции достаточно сложная для понимания штука.
А конкуренты в это время бегут вперёд.
О каких конкурентах идёт речь? D? Zig? Haskell? Idris? Go?
Not everyone prioritizes “safety” above all else. For example, in application domains where performance is the main concern, the P2687R0 approach lets you apply the safety guarantees only where required and use your favorite tuning techniques where needed. Partial adoption of some of the rules (e.g., rules for range checking and initialization) is likely to be important. Gradual adoption of safety rules and adoption of differing safety rules will be important. If for no other reason than the billions of lines of C++ code will not magically disappear, and even “safe” code (in any language) will have to call traditional C or C++ code or be called by traditional code that does not offer specific safety guarantees.
Нет, не додумал. Вот прямая цитата. Опять же рекомендую и отчёт АНБ прочитать.
Атаки через уязвимости в ПО существуют. И встречаются достаточно часто, чтоб считать это реальной проблемой. Что плохого в том, чтоб заранее избавиться от части из них и сосредоточиться на других векторах атаки (или на чём либо ещё)? Что плохого в использовании более подходящих инструментов для этого?
Страуструп предлагает писать программы, используя безопасное подмножество языка обложившись статическим анализом. И лишь в тех местах, для которых это не работает, нарушать/обходить эти правила, переходя на ручной режим.
В Rust по сути тоже самое, только удобнее. У вас есть безопасное подмножество языка с достаточно строгими правилами, которые проверяются на этапе компиляции. И есть ручной режим, в котором вы перекладываете ответственность с компилятора на себя. Различие только в том, что переход между тем и этим явный. При этом вы получаете безопасный вариант по умолчанию (с точки зрения кривой обучения), а к более сложным вещам приступаете только при возникновении необходимости. В C/C++ получается зеркально, сначала вы учитесь делать всё подряд, а только потом приходит понимание, знания, опыт.
А ответ на ваш вопрос написан у меня в профиле прямо под ником. ¯\_(ツ)_/¯
Термин не имеет чёткого законодательного определения. Часто используется в политической лексике и публицистике без конкретизации смысла. Из Википедии
Первыми приходят всякие боты по ключевым словам. Им за это миска риса и обещания кошкожена. Киберпанк, в котором мы живём.
Правило №0: Никому не доверяй. Особенно себе.
Прошло всего 80 дней с начала трагедии. Наше время, наши дни.
Сложить два числа - не странное. Сложить две строки - странное.
Тот факт, что интерфейс работы с числовой арифметикой не идентичен у Rust и C++ не является проблемой Rust или C++. Здесь только ваши заблуждения.
Тоже самое и в контексте перегрузки операторов. Семантика и практика применения другая. Вы упорно утверждаете, что оно обязано быть идентичным.
Многие инструменты при использовании приносят не только пользу, но и вред. Так у механизма исключений есть проблемы по части производительности и переносимости. Нецелевое использование перегрузки операторов порождает код, работа которого неочевидна. Тоже самое и для некоторых других штук.
Rust пытается своим дизайном предотвратить часть неправильного использования подобных инструментов. Может не всегда, но достаточно часто у него это получается. В конце концов для самых
упоротыхупорных есть инструменты для воплощения своих фантазий. Использование подобного будет сложнее использования более подходящего инструмента. И это сделано намерено.unsafe
означает, что вы обязаны проверить все инварианты вручную. Полностью внутри блокаunsafe
. Всё, что покидает блокunsafe
, обязано соответствовать save-гарантиям.Колбасу принято пихать в рот. Более того, на колбасе написано, что её в рот. Если вы, как разработчик, привыкли пихать колбасу в непредназначенные для этого отверстия, то чтож, вы всё ещё можете продолжать делать это. Кто я такой, чтоб осуждать вас. Но прошу вас, пожалуйста, не делайте этого на людях. И уж точно не стоит обвинять колбасу в том, что её диаметр не соответствует вашим ожиданиям. Тут я могу посоветовать только продолжать разработку.
Лишнее копирование на стек удаляется компилятором. Я не поленился и сходил проверить простенький случай. Потому и прошу вас привести пример, когда это не работает.
Поверхностный поиск показывает, что корректнее говорить не о том, что не реализовали, а о том, что не стабилизировали. Увы, детали пояснить не могу. Не являюсь тем, кто реализует эту фичу. Раз вы знаете больше, так объясните.
Не задумывался. Но теперь мне интересно и я готов послушать. Могу лишь предположить, что фича достаточно сложная, с нюансами.
Начинаю беспокоиться о вашем ментальном здоровье. Старайтесь соблюдать рекомендации психиатра. Я свои таблетки принял и рекомендую вам сделать тоже самое. Иначе опять санитар стучаться в палату будет.
В зависимости от контекста. В большинстве случаев писать нормальный код, который заранее валидирует входные данные. Компилятор скорее всего оценит ваши старания и хорошенько соптимизирует. Есть всякие
checked_div
/overflowing_div
/saturating_div
для некоторых специальных случаев.Если вы привыкли использовать перегрузку операторов для странного, то это это плохая привычка.
Уточню. В Rust ссылки висячими не бывают. Вы или компилятор обязаны гарантировать это.
Выше ответил. В Rust перегрузка операторов используется сильно реже и только в тех местах, где это необходимо. Скажем, складывание строк выглядит достаточно глупо.
Вы используете
unsafe
тогда и только тогда, когда компилятор не имеет возможности доказать что-то. unsafe fn или там unsafe trait означает, что помимо гарантий, предоставляемых Rust, вы ещё должны помнить о дополнительных ограничениях. Использованиеunsafe {}
означает, что вы удовлетворяете эти ограничения самостоятельно. Но ничего сверх этого. Механикаunsafe
не является инструментом оптимизаций или способом хакнуть систему. Только тем, что написано на этикетке.Приведите пример, когда это необходимо. В большинстве случаев всё равно нужно некое инициализирующее значение и его в любом случае придётся перемещать откуда-то. Часть простых случаев скорее всего итак оптимизируется. Для другой части есть
MaybeUninit
(включая инициализацию нулями).По той причине, что эту возможность ещё не реализовали.
Никогда не ощущал необходимости знать всё. Считаю незнание чего либо нормальным и естественным.
Обработка исключений может быть гораздо дороже. На некоторых архитектурах настолько, что вообще нет возможности их использовать. Опять же куча оптимизаций есть, которые могут превратить бранч во что-то условно бесплатное. В современных архитектурах есть branch prediction, а в современных компиляторах LTO, PGO и прочие ништяки.
Да не, просто один оператор
?
. Алсо чемmatch
вас смущает в сравнении с try-catch? Полагаю, тем, чтоmatch
это гораздо более мощный и гораздо более удобный инструмент.Тем, что всегда требуем явно писать возможные ошибки? Так это наоборот улучшение. Явное лучше неявного.
Бессмыслено натягивать эти термины на Rust с его move-семантикой. Лучше сходите и напишите ещё один копирующий конструктор.
Никак. Математические операторы предназначены для штук, подобных математическим примитивам. Строки математическими примитивами не являются. Об этом всём сказано в документации. Для хитрой конкатерации строк есть макрос
format_args!
и куча разных штук поверх него.Я не говорю о том, что
unsafe
применять нельзя вообще. Это очень важный и очень сложный инструмент. Но нужен подобный инструментарий редко и требует дополнительной квалификации. Тоже самое в разной степени касается, например, перегрузки операторов и процедурных макросов (впрочем и обычных макросов это тоже касается). Почти всегда вы избегаете использования подобных инструментов. Кроме тех случаев, когда без них не обойтись. Это как наклейка "осторожно, высокое напряжение" на трансформаторной будке. Перед тем, как полезть внутрь, убедитесь что вы электрик.Немного другой пример. Трейты являются практически полноценной реализацией ООП со всеми стандартными наворотами включая множественное наследование. При этом Rust определённо не является ООП языком.
Нет. Вместо этого вы делаете метод, который возвращает
Option
/Result
. Я всё понимаю, микроскоп красивый, новенький, блестящий. Однако всё же не стоит забивать им гвозди.Вообще это довольно важная часть дизайна на мой взгляд. Rust старается оградить вас от неправильного использования собственного инструментария. В некотором смысле это также реализация принципа "не плати за то, что не используешь", но в плане когнитивной сложности.
Вместо того, чтоб писать какие-то хитрые штуки с возвратом ошибок из перегруженный операторов вы просто паникуете там + пишете методы. А оператор может быть тупо вызовом метода +
unwrap
. Ну или не перегружать ничего. И вам не нужно держать в голове особенности поведения конкретной реализации определённого оператора. Вместо этого вы можете один раз запомнить общий случай.Паники отличаются от исключений тем, что единственный адекватный способ их "обработать" - написать корректный код. При схожести механизма работы, парадигма совершенно другая. Но у вас всё ещё есть возможность их ловить в очень специфических случаях.
Понятия не имею если честно. Не изучал этот вопрос. Буду рад, если вы приведёте реалистичный пример, где это вообще может быть нужно, std::mem::MaybeUninit недостаточно, а другими способами не реализуемо.
Есть что-то забавное в том, что в последнем примере всё решилось удалением unsafe-кода. Соблюдать правило "не писать unsafe без крайней необходимости" проще, чем следить за UB в C++ коде. Намного проще. На порядки.
Опять же важно не наличие подобных ошибок и даже не их количество, а простота их обнаружения и исправления. Проверка условного десятка строк с пометкой unsafe сильно дешевле полного аудита всего кода.
И кстати, вы не совсем правы на счёт того, что паники нельзя отловить. Всё немножечко сложнее, чем просто можно/нельзя. За подробностями рекомендую читать документацию. Ну и замечу, что не смотря сильное сходство, паники не являются аналогом исключений.
Прочитайте внимательнее отчёт АНБ. Они говорят о том, что вот же куча разных ЯП, оставьте няшную сишечку и плюсы для специальных случаев.
А насчёт универсальности - она работает, но только тогда, когда имеет очевидное превосходство. Опять же USB вполне себе стал стандартом, как контрпример про разъёмы. Английский язык в некоторых областях тоже что-то вроде негласного стандарта (например в научном мире). Система СИ так или иначе используется везде в технической/научной среде. Во всех этих случаях оно работает только в областях, где есть какие-то причины для этого.
Скажем, нет какой-то особой разницы между дюймами, миллиметрах или бананами в дисплеях. Когда вы их продаёте. А вот когда вы производите дисплеи, то там будут использоваться единицы СИ.
Полагаю, это уже лично мне претензия. Не очень понимаю, с чего вы взяли, что Rust вообще в принципе предлагает что-то выдающееся. С моей точки зрения это хорошо сделанный инструмент. Весьма неплохо себя показывает, если вам нужно достичь надёжности и производительности одновременно, но вы готовы заплатить сложностью. Опять же как и у любого другого ЯП, у Rust есть области, для которых оный подходит плохо. Например для приложений, где нативный GUI является очень важной частью, я Rust крайне не рекомендую.
Полагаю, я всё же должен уточнить о вашем опыте использования Rust. Здесь есть относительно высокий порог вхождения. Как минимум прочитать основную документацию (включая оффициальные учебные пособия) и написать некоторое количество кода. Впрочем это характерно и для многих других ЯП, которые содержат какие-то новые для вас парадигмы. Borrow checker не смотря на кажущуюся простоту концепции достаточно сложная для понимания штука.
О каких конкурентах идёт речь? D? Zig? Haskell? Idris? Go?
Нет, не додумал. Вот прямая цитата. Опять же рекомендую и отчёт АНБ прочитать.
Атаки через уязвимости в ПО существуют. И встречаются достаточно часто, чтоб считать это реальной проблемой. Что плохого в том, чтоб заранее избавиться от части из них и сосредоточиться на других векторах атаки (или на чём либо ещё)? Что плохого в использовании более подходящих инструментов для этого?
Страуструп предлагает писать программы, используя безопасное подмножество языка обложившись статическим анализом. И лишь в тех местах, для которых это не работает, нарушать/обходить эти правила, переходя на ручной режим.
В Rust по сути тоже самое, только удобнее. У вас есть безопасное подмножество языка с достаточно строгими правилами, которые проверяются на этапе компиляции. И есть ручной режим, в котором вы перекладываете ответственность с компилятора на себя. Различие только в том, что переход между тем и этим явный. При этом вы получаете безопасный вариант по умолчанию (с точки зрения кривой обучения), а к более сложным вещам приступаете только при возникновении необходимости. В C/C++ получается зеркально, сначала вы учитесь делать всё подряд, а только потом приходит понимание, знания, опыт.
А ответ на ваш вопрос написан у меня в профиле прямо под ником. ¯\_(ツ)_/¯
Звучит так, будто Страуструп рекомендует Rust.
На одного товарища майора.
Судя по первой картинке у них там и текстуры плохо подгружаются.