Comments 36
Для арифметики можно использовать wrapping-методы или даже тип-обёртку
Wrapping.Для доступа к памяти можно использовать указатели, раз у нас уже всё равно
unsafeи мы полагаемся на "обещания о разумности кода". Анализировать корректность явного доступа к памяти будет проще, чемassert, спрятанный вcheck_invariant.
Хорошо бы в Rust в принципе упростить возможность работать в пределах такого подмножества языка, которое не предполагает паник
можно просто взять С++ или С
оптимизатор Rust.
это называется LLVM )
Взять С++ и получить и получить UB в миллионе мест, это конечно то что надо для системной библиотеки.
А ничего что в расте разное поведение на релизе и на дебаге буквально в обсуждаемом месте (в релизе ничего не делают, а в дебаге кидают панику), то есть поведение наблюдаемое меняется. Это ж абсурд
Можно настроить одинаковое поведение
Вы никогда не слышали про debug assert? И ничто не мешает отключить его.
Дело в том, что Rust говорит, что переполнение знаковых чисел не является undefined behavior. А потом у вас разное поведение на дебаге и релизе для переполнения знаковых
Это непоследовательность
а где будут УБ, ллвм, технически ничего лучше не сгенерирует чем то что она умеет, а это значит в С++ всё зависит от подхода программирования
я уверен что можно так же свой слой трансляции написать, который как и раст делает импл к структрутре но с указателем, и так же обеспечить безопасность
LLVM это всё же бэкэнд, которые кушает промежуточный код, поэтому UB возникает на уровне компилятора. На с++ можно писать так, чтобы избегать UB, но это довольно сложно и требует довольно обширной машинерии - тесты, фаззинг, статические анализаторы, про покрытие думать. Ну и стандартная библиотека С++ абсолютно точно под это не заточена, поэтому придётся использовать, что-то сторонее и вероятно страдать от увеличения времени компиляции из-за тонны шаблонов. Собственно, как-от так и появляются библиотеки типа flux или beman. Остаётся гадать найдется ли достаточно библиотек, чтобы покрыть все требования к вашему проекту или придётся писать недостающее самому.
у меня по покрытию так выходит, плюс есть валгринд и асан, scan-build, clangd
Скрытый текст

ну там смотря как писать(так придётся тогда математику свою писать, всю библиотеку переписывать ради premature optimisation), если аккуратно, то можно что-то придумать, а у Раста какое преимущество, байткод Раст не сгенерирует такой какого нету - какой не поддерживает внутрянка подлинкованная к llvm
В расте банально проще писать тесты. Причем любых мастей - юниты, интеграцию, фаззинг/пропы. Открытых аналогов какого-нибудь kani для С++ и вовсе не встречал, хотя если не ошибаюсь существуют коммерческие решения и штуки типа TLA+ (например, доклад), но это уже сам по себе отдельный язык.
У Rust статический анализатор фактически часть компилятора. Для unsafe кода есть интерпретатор miri вместо asan/ubsan.
Ну в C++ хотя бы cout << "Hello, World!"; не паникует =)
Пример смешной, а ситуация страшная. По факту, неявные вызовы паники, вставляемые компилятором, это UB.
Панику не выдаст, он выдаст эксепшен если устройство не доступно или UB если вызов вне мейна, например в деструкторе когда std::cout уже разрушен. Конечно "это другое"(с)
он выдаст эксепшен
При дефолтных параметрах С++ не выкидывает тут исключение.
Но проблема не специфичная для Раста. Неявные вызовы везде неприятны (в других ЯП, например) :
неявные эксепшны
неявные выделения памяти, которые могут ещё и обломиться
неявный вызов GC
неявный вызов деструктора не вовремя
Нужно иметь механизмы контроля. Непонятно, почему автора не устроило все же #[no_panic], лень писать?
поидее можно его проверить же
...
std::ostream *stream;
...
std::string_view output{t};
...
if(file.stream){//cout у меня в одной структуре оба
*file.stream << output << std::endl;
}у юнита std::ostream есть fail bool, exceptions, bad, eof итд
Когда раст уберет отовсюду unsafe кроме системных вызовов, тогда можно поговорить про его особенность. Пока раст тот же ub, просто в гораздо меньшем количестве, и оно не уменьшается.
в гораздо меньшем количестве
На фоне сишечек — уже огромное достижение
так на си можно тоже дописать слой трансляции(даже не побоюсь этого) по управлению указателями
тоесть
int a=1;
int *b = &a;
всё вокруг этого да?
по этой причине С быстрее Раста скорее всего
а если не так делать, а так то вроде медлнее но я не замерял
Вы о чём вообще?
Код по ссылке легко валится написанием какого-нибудь банального *(SafePtr((int*)666)) = 666;
На Rust вы не сможете так сделать, не используя unsafe — тем Rust и прекрасен
а щас https://godbolt.org/z/oddrTfcYf, наверно вы правы на С++ придётся попотеть наверно
А что принципиально изменилось? Просто убираем = 666 и получаем free(): invalid pointer
https://godbolt.org/z/sP9oEYnbM
я на голых структурах и голых указателях пишу и не пишу такие конструкции как вы показали или как в той статье на английском языке
А еще на Rust нельзя написать L1 список без unsafe...
А какая связь между unsafe и ub?
Каша их топора какая-то получилась. Автор избавился от panic добавив assert_unchecked, что определённо хуже, т. к. последнее в случае ошибки в коде тихо завалит или попортит программу. Зачем спрашивается? Чтобы выкинуть 300 кб кода? Так можно было бы вместо этого тупо отключить размотку стека и добавить свой минималистичный обработчик panic, а не заниматься казуистикой.
Чтобы выкинуть 300 кб кода?
Да, пытается засунуть в эмбеддед с сохранением надежности, а оно не лезет)
Так можно было бы вместо этого тупо отключить размотку стека и добавить свой минималистичный обработчик panic, а не заниматься казуистикой.
Тут вспоминаются языки в которых существует Trap-handler
Автор избавился от panic добавив assert_unchecked, что определённо хуже, т. к. последнее в случае ошибки в коде тихо завалит или попортит программу.
Так ошибки не будет в данном коде никогда. Вставка assert_unchecked - это декларация инварианта для структуры S. Структуру S мы можем создать только одним способом - через вызов S::new, который проверяет на корректность входные данные.
Структуру S мы можем создать только одним способом
А потом добавляем метод, который иногда нарушает инвариант...
Это стандартный подход в расте: прячем все unsafe-операции за безопасным API, которое будет проверять входные данные и убеждаться в том, что никакие инварианты нарушены не будут. Ничего лучшего вы придумать не сможете, если хотите достичь заявленных целей в статье.
Но данная оптимизация в принципе редко применима на практике, это правда. В прикладных программах, очевидно, так никто делать не станет: код становится слишком сложнее. Цена за выигрыш слишком высока, всем плевать на лишние 300кб.
Может быть и так, тоже вариант. Но, как известно, "С дуру можно и хрен сломать, хоть он и без костей!" (С).
Автор выбрал компромисс. Компромисс его устроил и он поделился с сообществом. Вай нот? По мне -- отличная идея!
Rust без паник: дельная техника для системного программирования