Comments 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 можно копировать таким простым образом.
А, перечитал.
...simply replacing the name of the const with its value
. Т.е. const — это такой #define
в rust, внутри, каждое использование — это отдельный static объект (по сравнению с static переменной, которая общая для всех мест, которые её используют).
Спасибо.
const — это частный случай того, что можно скопировать (даже если в общем случае контейнер нельзя скопировать: если написать Some(vec!(1,2,3)) оно не скомпилируется на строчке с const).
Rust 1.50.0: улучшение индексации массивов, безопасность полей объединений и усовершенствование файловых дескрипторов