Comments 27
"Ни разу не возникало ощущения, что Rust каким-то образом сдерживает меня или что я жертвую производительностью или функциями, потому что использую Rust, а не C" — ну да, а те же приседания с get_unchecked в unsafe блоках — это не оно.
И где же в использовании get_unchecked
жертвование производительностью или возможностями (в оригинале capabilities)? Если бы было про краткость/лаконичность ещё можно было бы согласиться, но в текущем варианте, нет, не оно.
В самом факте, что приходится использовать его вместо стандартной [] нотации, которая как раз и вызывает проблемы с производительностью. Впрочем, вашего права иметь на этот счёт свое мнение я не оспариваю.
Ну есть выбор — медленнее, но с проверками или быстрее, но без проверок. Вот если бы опции "быстрее, но без проверок" в расте не было, но он бы действительно "каким-то образом сдерживал".
Мне кажется, что, начиная с опредеённого размера кодовой базы, это скорее вредный подход. Обычно у нас есть критичные к производительности места, которые можно внимательно проанализировать и решить, что там проверки не нужны. Со всем кодом так обычно не поступают.
У автора всё-таки особенный случай.
Кажется, понял я как раз правильно и в том и проблема, что проверки отключаются сразу по всему проекту. И если отдельны части написаны аккурано и обложены тестами, но на весь код это не распространяется.
можно реализовать и неотключаемые проверки, но, на мой взгляд, реальная потребность в них возникает довольно редко (если мы говорим о приложениях, в которых производительность важна).
Давайте определимся, что значит "отключаем проверки"? Получаем UB, если вдруг что-то пошло не так? В таком случае, проверки очень даже нужны.
На примере get_unchecked из статьи: в идеальном случае мы можем передать компилятору знание о том, что проверка не нужна (например, если у нас есть итератор) — это будет ещё и безопасно. В некоторых случаях мы обладаем знанием, что операция безопасна, а компилятор нет. В этом случае используем "костыли" (get_unchecked) и обкладываем код тестами. Делать так везде скорее вредно: утверждения, которые мы в уме держим (тут массив точно больше десяти элементов!) могут быть ошибочны.
У автора весь смысл проекта сделать как можно быстрее и меньше. В других проектах обычно коректность и отсутствие порчи данных важны.
#ifdef NDEBUG
#define assert(expr)
#else
#define assert(expr) if (!(expr)) { <some logging and debug break>; }
#endif // #ifdef NDEBUG
Для релиза вместо no-op'а можно еще хинты для оптимизатора поставить, вроде как уже ввели стандартизированный способ.
Да, в релизе это может добавить UB, только вот на стадии тестирования подавляющая их часть будет отловлена. Если же по какой-то причине надо отдать в продакшен какую-то нетестированную часть проекта — просто переопределите макрос так, что бы в релизе было то же, что в дебаге, и получайте логи об ошибках.
В общем то я уверен, что аналогичные дела можно реализовать в Rust, но, насколько я понимаю, стандартные паники, на которых написана стандартная библиотека и, думаю, огромная часть нестандартных, таки не подлежат отключению (как минимум автор не нашел способа это сделать).
Да, в релизе это может добавить UB, только вот на стадии тестирования подавляющая их часть будет отловлена.
Не будет.
Так я о том и говорю, что с таким подходом можно или отключить все проверки сразу или включить. Можно, конечно, определять дефайны для отдельных частей проекта, но с таким сталкивался редко. Ну а дальше всё упирается в устраивает ли нас утверждение "(думаем, что) большая часть UB отловлена". Могу только повторить, что предпочитаю уменьшать объём кода надёжность которого гарантируется "вручную".
Раньше любил дебаг асертами (которые в расте тоже есть) код обкладывать, потом пришёл к тому, что это полумера и, зачастую, бессмысленная "оптимизация". Да, бывают исключения, но вообще оптимизировать лучше с профайлером и проверки редко бывают узким местом.
> Rust
Демосцена уже не та.
Я считываю тон вашего комментария, но не улавливаю его смысл. Не могли бы вы раскрыть смысл?
Меня очень возмутила сборка раста из коробки на 180 кб, сократить ее значительно оказалось возможно, но до 4 кб дойти выглядит подвигом.
Хотя понятно, что стандартная библиотека.
Вот 10 место:
www.youtube.com/watch?v=5k0OiT8X3z0
Вы можете сказать, что компетишны разные. А какая разница?
Другая, гораздо более трудная для понимания проблема с идиоматическим циклом Rust заключается в том, что в некоторых случаях компилятор добавлял некоторый дополнительный код настройки итератора, который действительно раздувал код. Я так и не понял, что вызывает эту дополнительную настройку итератора
Дело в том, что подобный цикл
for x in 0..10 {
// do code
}
Развернется в следующий код:
match IntoIterator::into_iter(0..10) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = {
// do code
};
},
}
let mut x = 0;
loop {
// do code
x += 1;
if x == 10 {
break;
}
}
ps: то есть это не совсем демо — это игра, по Марсу можно летать =)
cloud.mail.ru/public/4Taj/2zMm29QK9
Как я написал интро 4K на Rust — и оно победило