Как стать автором
Обновить

Rust 1.50.0: улучшение индексации массивов, безопасность полей объединений и усовершенствование файловых дескрипторов

Время на прочтение4 мин
Количество просмотров5.5K
Всего голосов 36: ↑35 и ↓1+34
Комментарии15

Комментарии 15

Что-то меня "inline const" смущает. RFC почитал, но всё равно кажется, что требование константности компилятор всё равно мог бы сам выводить без этого "костыля". Переубедите?..

Вывести — может. Объяснить, где именно ошибка — не всегда. Выражение может содержать вызовы чужих функций, сигнатуры которых могут измениться (сегодня const, завтра нет). Словом "const" мы говорим тогда, что ошибка содержится внутри выражения, а не в контексте его использования.

Честно говоря, не понял, можно разжевать? Пример из RFC:


match x {
    const { 3.pow(3) } => println!("three cubed"),
    _ => {}
}

Правильно я понимаю, что с явным const { ... } компилятор может уверенно сказать, что выражение не константное? А без явного указания просто скажет, что паттерн неправильный? Но это не кажется большой проблемой, если честно.


Ну и, кстати, если функция вдруг станет не константной — это ведь ломающее изменение. С таким же успехом может тип или количество параметров измениться и тоже "всё поломать".

В обоих случаях компилятор может уверенно сказать, что выражение константное, если pow() объявлена как константная. И в обоих случаях выдаст ошибку, если нет. Но по-разному укажет на причину ошибки. При автоматическом выведении ошибка будет "ой, это выражение не константное, его нельзя сюда ставить", а во втором — "такая-то функция недопустима в константных выражениях".


В случае match, инициализации static или если в левой части знака равенства стоит const компилятор в принципе может сделать то же самое и без явного const. В других случаях — нет, там непонятно, какая часть выражения ожидается константной, и с какого места разрешено runtime-вычисление. Это существенно потому, что Rust имеет право копировать константы как угодно, даже если соответствующий тип не Copy, у константы нет определенного места в памяти (указатель взять нельзя). Отдельно это важно в embedded, где процессор может быть и 8-битным, а вычисление в скобках — тяжелым. Тогда программа просто не будет правильно работать без compile-time-вычисления, даже если синтаксически оно допустимо:


   let y = PI.cos();

"Все поломать" можно многими способами, но мы хотим, чтобы компилятор показал на то место, которое сломали, а не на случайно попавшую под раздачу совсем другую строку.

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

Согласен, но компилятор мог бы ведь и говорить почему выражение не константное?


Хотя мне всё-таки кажется, что в inline const пихать сложные выражения — проще тогда уже обычную константу объявить. И вот именно с этой точки зрения мне const { ... } не нравится: вроде как, дают возможность более лаконично записать, но при этом надо эту конструкцию городить.


let y = PI.cos();

Не понял именно этот пример: если нужна именно константа, то и писать надо const y, но мысль, кажется, уловил.


Согласен, явный const { ... } иногда был бы полезен как явное требование, но я всё-таки предпочёл бы, чтобы если константность в выражении была обязательной, то компилятор сам бы пытался вычислить его во время компиляции.

Я не совсем понимаю, что получается в результате вот этого:


    const EMPTY: Option<Vec<i32>> = Some(Vec::new());
    let mut empties = [EMPTY; 10];

Что будет, если я сделаю


empties[0].as_mut().unwrap().push(1);
println!("{:?}", empties[1]);

?
Я проверил — второй элемент будет независимым (т.е. в выводе будет пусто). Но мне это совсем не очевидно, потому что "10 раз того же самого" для контейнера двусмысленно.

Дока нам говорит следующее:


A repeat expression [x; N], which produces an array with N copies of x. The type of x must be Copy.

А про Copy говорится:


Types whose values can be duplicated simply by copying bits.

То есть, массив заполняется копиями и они обязаны быть независимыми. Логично, что константность не ломает это поведение, просто раз можно создать один экземпляр объекта, то можно и создать 10 (независимых).

Вот, да, я про это и говорю. Теперь, когда у нас есть тип с Copy, всё просто. Сказали 10 раз Copy, получили 10 раз Copy.


Но вот в примере выше у нас же Vec — не Copy. Так что что именно происходит мне не совсем понятно. Они каким-то образом понимают (как?) что вот этот конкретный пустой Vec, хоть он не Copy, но его можно memcpy?

Ну я всё-таки предполагаю, что наблюдаемое поведение должно сохраняться. То есть, под капотом компилятор может создать нужное количество объектов без использования Copy.

Но вот как он понимает, что от повторного создания объекта вселенная не взорвётся?

Они это понимают на этапе создания const.
Всё, что const можно копировать таким простым образом.

А, перечитал.


...simply replacing the name of the const with its value. Т.е. const — это такой #define в rust, внутри, каждое использование — это отдельный static объект (по сравнению с static переменной, которая общая для всех мест, которые её используют).


Спасибо.

Вам спасибо за аналогию. Мне стало понятнее.

Он всегда копирует элементы (и для этого необходима копируемость того, что вы кладёте при инициализации «тем же самым»).
const — это частный случай того, что можно скопировать (даже если в общем случае контейнер нельзя скопировать: если написать Some(vec!(1,2,3)) оно не скомпилируется на строчке с const).
Эх, когда же уже перенесут std::lazy в stable ветку…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации