Команда Rust рада сообщить о выпуске новой версии — 1.50.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.
Если вы установили предыдущую версию Rust средствами rustup
, то для обновления до версии 1.50.0 вам достаточно выполнить следующую команду:
rustup update stable
Если у вас ещё не установлен rustup
, вы можете установить его с соответствующей страницы нашего веб-сайта, а также посмотреть подробные примечания к выпуску на GitHub.
Что вошло в стабильную версию 1.50.0
В этом выпуске мы улучшили индексацию массивов, повысили безопасность доступа к полям объединений, усовершенствовали файловые дескрипторы и добавили их в стандартную библиотеку. Смотрите подробные примечания к выпуску, чтобы узнать о других изменениях, не представленных в данном анонсе.
Константные обобщения при индексации массива
Продолжая движение к стабилизации константных обобщений, этот выпуск добавляет реализации ops::Index
и IndexMut
для массивов [T; N]
любой длины const N
. Оператор индексации []
уже работал с массивами с помощью встроенной магии компилятора, но на уровне типа массивы до сих пор фактически не реализовывали библиотечные типажи.
fn second<C>(container: &C) -> &C::Output
where
C: std::ops::Index<usize> + ?Sized,
{
&container[1]
}
fn main() {
let array: [i32; 3] = [1, 2, 3];
assert_eq!(second(&array[..]), &2); // срезы работали ранее
assert_eq!(second(&array), &2); // теперь это работает напрямую
}
const
повторение значений массива
Массивы в Rust могут быть записаны как в форме списков [a, b, c]
, так и в форме повторений [x; N]
. Повторения разрешены для длины N
большей, чем один, только для x
, реализующих типаж Copy
, и в рамках RFC 2203 мы стремились разрешить любые const
выражения. Однако пока эта функциональность была нестабильна для произвольных выражений, лишь начиная с Rust 1.38 её реализация случайно позволила использовать const
значения в повторениях массивов.
fn main() {
// Это не разрешено, так как `Option<Vec<i32>>` не реализует `Copy`.
let array: [Option<Vec<i32>>; 10] = [None; 10];
const NONE: Option<Vec<i32>> = None;
const EMPTY: Option<Vec<i32>> = Some(Vec::new());
// Однако повторения с `const` значениями разрешены!
let nones = [NONE; 10];
let empties = [EMPTY; 10];
}
В Rust 1.50 эта возможность признана официально, так что вы можете использовать такие конструкции без опасений. В будущем для того, чтобы избежать "временных" именований констант, вы можете использовать встроенные выражения const
согласно RFC 2920.
Безопасные присвоения полям объединения ManuallyDrop<T>
В Rust 1.49 появилась возможность добавлять поля ManuallyDrop<T>
в union
, позволяющая таким образом использовать Drop
для объединений. Однако объединения не сбрасывают старые значения во время присваивания полей, так как не знают, какой из вариантов ранее был действителен. Поэтому из соображений безопасности Rust ранее ограничивался только типами Copy
, которые не используют Drop
. Разумеется, ManuallyDrop<T>
не требует Drop
, поэтому теперь Rust 1.50 расценивает присваивания и этим полям как безопасные.
Ниша для File
на Unix платформах
У некоторых типов в Rust есть определённые ограничения на то, какое значение считать допустимым, поскольку они могут выходить за границы допустимых значений диапазона памяти. Мы называем любое оставшееся недопустимое значение нишей (niche), и это пространство может быть использовано для оптимизации схемы размещения типов. Например, в Rust 1.28 мы представили целочисленные типы NonZero
, такие как NonZeroU8
, где 0
— ниша. Это позволило Option<NonZero>
использовать 0
, чтобы представить None
без использования дополнительной памяти.
На Unix-платформах File
— это просто системный целочисленный файловый дескриптор. Это значит, что у нас есть возможная ниша — ведь он никогда не может быть -1
! Системные вызовы, которые возвращают файловый дескриптор, используют -1
для обозначения того, что произошла ошибка (проверяется errno
), так что -1
никогда не будет действительным файловым дескриптором. Начиная с Rust 1.50 эта ниша добавлена в определение типа и тоже может быть использована для оптимизации размещения значения в памяти. И следовательно Option<File>
теперь имеет такой же размер, как и File
!
Изменения в стандартной библиотеке
В Rust 1.50.0 были стабилизированы следующие 9 функций:
bool::then
btree_map::Entry::or_insert_with_key
f32::clamp
f64::clamp
hash_map::Entry::or_insert_with_key
Ord::clamp
RefCell::take
slice::fill
UnsafeCell::get_mut
И довольно много существующих функций стало const
:
IpAddr::is_ipv4
IpAddr::is_ipv6
Layout::size
Layout::align
Layout::from_size_align
pow
для всех целочисленных типовchecked_pow
для всех целочисленных типовsaturating_pow
для всех целочисленных типовwrapping_pow
для всех целочисленных типовnext_power_of_two
для всех беззнаковых целочисленных типовchecked_power_of_two
для всех беззнаковых целочисленных типов
Смотрите подробные примечания к выпуску для более детальной информации.
Другие изменения
Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения.
Участники 1.50.0
Множество людей собрались вместе, чтобы создать Rust 1.50.0. Мы не смогли бы сделать это без всех вас. Спасибо!
От переводчиков
С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.
Также можете поддержать нас на OpenCollective.
Данную статью совместными усилиями перевели andreevlex, TelegaOvoshey, blandger, nlinker и funkill.
DarkEld3r
Что-то меня "inline const" смущает. RFC почитал, но всё равно кажется, что требование константности компилятор всё равно мог бы сам выводить без этого "костыля". Переубедите?..
agalakhov
Вывести — может. Объяснить, где именно ошибка — не всегда. Выражение может содержать вызовы чужих функций, сигнатуры которых могут измениться (сегодня const, завтра нет). Словом "const" мы говорим тогда, что ошибка содержится внутри выражения, а не в контексте его использования.
DarkEld3r
Честно говоря, не понял, можно разжевать? Пример из RFC:
Правильно я понимаю, что с явным
const { ... }
компилятор может уверенно сказать, что выражение не константное? А без явного указания просто скажет, что паттерн неправильный? Но это не кажется большой проблемой, если честно.Ну и, кстати, если функция вдруг станет не константной — это ведь ломающее изменение. С таким же успехом может тип или количество параметров измениться и тоже "всё поломать".
agalakhov
В обоих случаях компилятор может уверенно сказать, что выражение константное, если pow() объявлена как константная. И в обоих случаях выдаст ошибку, если нет. Но по-разному укажет на причину ошибки. При автоматическом выведении ошибка будет "ой, это выражение не константное, его нельзя сюда ставить", а во втором — "такая-то функция недопустима в константных выражениях".
В случае match, инициализации static или если в левой части знака равенства стоит const компилятор в принципе может сделать то же самое и без явного const. В других случаях — нет, там непонятно, какая часть выражения ожидается константной, и с какого места разрешено runtime-вычисление. Это существенно потому, что Rust имеет право копировать константы как угодно, даже если соответствующий тип не Copy, у константы нет определенного места в памяти (указатель взять нельзя). Отдельно это важно в embedded, где процессор может быть и 8-битным, а вычисление в скобках — тяжелым. Тогда программа просто не будет правильно работать без compile-time-вычисления, даже если синтаксически оно допустимо:
"Все поломать" можно многими способами, но мы хотим, чтобы компилятор показал на то место, которое сломали, а не на случайно попавшую под раздачу совсем другую строку.
DarkEld3r
Согласен, но компилятор мог бы ведь и говорить почему выражение не константное?
Хотя мне всё-таки кажется, что в inline const пихать сложные выражения — проще тогда уже обычную константу объявить. И вот именно с этой точки зрения мне
const { ... }
не нравится: вроде как, дают возможность более лаконично записать, но при этом надо эту конструкцию городить.Не понял именно этот пример: если нужна именно константа, то и писать надо
const y
, но мысль, кажется, уловил.Согласен, явный
const { ... }
иногда был бы полезен как явное требование, но я всё-таки предпочёл бы, чтобы если константность в выражении была обязательной, то компилятор сам бы пытался вычислить его во время компиляции.