Команда Rust рада сообщить о выпуске новой версии, 1.40.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.
Если вы установили предыдущую версию Rust средствами rustup
, то для обновления до версии 1.40.0 вам достаточно выполнить следующую команду:
$ rustup update stable
Если у вас ещё не установлен rustup
, вы можете установить его с соответствующей страницы нашего веб-сайта, а также посмотреть подробные примечания к выпуску на GitHub.
Что вошло в стабильную версию 1.40.0
Основными новшествами являются введение атрибута #[non_exhaustive]
, улучшения macros!()
и #[attribute]
. Наконец, миграционные предупреждения анализатора заимствований стали ошибками в Rust 2015. Смотрите подробности выпуска для дополнительной информации.
#[non_exhaustive]
структуры, перечисления и варианты перечислений
Предположим, вы являетесь автором библиотеки alpha
, которая содержит pub struct Foo
. Вы хотели бы сделать поля структуры alpha::Foo
публичными, но не уверены, придётся ли вам в будущих выпусках добавить больше полей в Foo
. Возникает дилемма: либо вы делаете поля приватными с последующими неудобствами, либо вы рискуете поставить пользователей в зависимость от полей и потом нарушит их код при добавлении новых. В Rust 1.40.0 представлен способ решить проблему с помощью #[non_exhaustive]
.
Атрибут #[non_exhaustive]
прикрепляется к структуре или варианту перечисления и препятствует полному сопоставлению полей, созданию упомянутой структуры или варианта вне крейта с их объявлением. Следующий пример демонстрирует ошибки в крейте beta
, зависящего от alpha
:
// alpha/lib.rs:
#[non_exhaustive]
struct Foo {
pub a: bool,
}
enum Bar {
#[non_exhaustive]
Variant { b: u8 }
}
fn make_foo() -> Foo { ... }
fn make_bar() -> Bar { ... }
// beta/lib.rs:
let x = Foo { a: true }; //~ ОШИБКА
let Foo { a } = make_foo(); //~ ОШИБКА
let Foo { a, .. } = make_foo(); //~ OK
// -- `beta` все еще будет компилироваться при добавлении полей.
let x = Bar::Variant { a: 42 }; //~ ОШИБКА
let Bar::Variant { b } = make_bar(); //~ ОШИБКА
let Bar::Variant { b, .. } = make_bar(); //~ OK
// -- `beta` все еще будет компилироваться...
Что же происходит за кулисами? Видимость конструкторов для #[non_exhaustive]
структуры или варианта перечисления будет понижена до pub(crate)
, тем самым запрещая их использование в сторонних крейтах.
Возможно, что более важным аспектом #[non_exhaustive]
является то, что атрибут может быть прикреплён к самим перечислениям. Вот код, взятый из std::cmp::Ordering
:
#[non_exhaustive]
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }
В данном случае #[non_exhaustive]
гарантирует возможность добавления новых вариантов в будущем. Это достигается запретом другим пакетам к использованию исчерпывающего сопоставления с образом для Ordering
. Компилятор бы отклонил следующее:
match ordering {
Relaxed | Release | Acquire | AcqRel | SeqCst => { /* logic */ }
//~^ ОШИБКА; если новый вариант был бы добавлен,
// это сломалось бы, если ошибки не было бы с самого начала.
}
Вместо этого другие пакеты теперь должны учитывать возможность появления новых вариантов перечисления, например добавляя подстановочный знак _
:
match ordering {
Relaxed | Release | Acquire | AcqRel | SeqCst => { /* logic */ }
_ => { /* logic */ } // OK; если будут добавлены новые варианты, ничего не сломается.
}
Подробная информация об атрибуте #[non_exhaustive]
доступна в отчёте о стабилизации.
Улучшения макросов и атрибутов
В 1.40.0 мы внесли несколько улучшений в макросы и атрибуты, включая:
-
Вызов процедурных макросов mac!()
в контекстах типов.Например, можно написать
type Foo = expand_to_type!(bar);
гдеexpand_to_type
будет процедурным макросом. -
Макросы в extern { ... }
блоках.Этот блок включает макросы
make_item!()
. Например:macro_rules! make_item { ($name:ident) => { fn $name(); } } extern { make_item!(alpha); make_item!(beta); }
Атрибутные процедурные макросы для элементов в
extern { ... }
блоках теперь также поддерживаются:extern "C" { #[my_identity_macro] //~ Давайте предположим, что это расширяется до `fn foo();`. fn foo(); }
-
Генерация macro_rules!
элементов в процедурных макросах.Макросы с синтаксисом функций (
mac!()
) и атрибуты (#[mac]
) теперь могут генерировать элементыmacro_rules!
. Подробнее смотрите в прилагаемом отчёте о стабилизации. -
Теперь сопоставление с помощью $m:meta
поддерживаетпроизвольные значения TokenStream
.То есть следующий код является корректным:
macro_rules! accept_meta { ($m:meta) => {} } accept_meta!( my::path ); accept_meta!( my::path = "lit" ); accept_meta!( my::path ( a b c ) ); accept_meta!( my::path [ a b c ] ); accept_meta!( my::path { a b c } );
Миграционные предупреждения анализатора заимствований становятся ошибками в редакции Rust 2015
В выпуске 1.35.0 мы сообщили, что NLL
появился в редакции Rust 2015 после первого выпуска для 2018 редакции в Rust 1.31.
Как мы сказали, старый анализатор заимствований мог допустить небезопасное управление памятью, и с помощью нового анализатора (NLL borrow checker) эти недочёты были решены. Так как эти ошибки могли нарушить работу стабильного кода, мы решили постепенно вводить эти ошибки, проверяя разрешит ли сборку программы старый анализатор, и запретит ли её новый. В этих случаях ошибки заменялись предупреждениями.
Предыдущий выпуск Rust 1.39.0 заменил эти предупреждения на ошибки для кода с 2018 редакцией. Rust 1.40.0 применит те же самые изменения для кода 2015 редакции, навсегда закрывая эти дыры в безопасности. Вместе с этим компилятор даже почистили от старого кода!
Если ваш проект не собирается из-за вышеописанных изменений, или вы хотите узнать больше, читайте пост Niko Matsakis's.
Больше константных функций в стандартной библиотеке
Начиная с Rust 1.40.0, следующая функция помечена как константная (const fn
):
Стабилизированные функции в стандартной библиотеке
В Rust 1.40.0 были стабилизированы следующие функции и макросы:
-
Более короткая, запоминающаяся и удобная версия макроса
unimplemented!()
. -
Создаёт
Vec<T>
изn
повторений среза. -
Эта функция забирает значения из изменяемой ссылки и заменяет их значением по умолчанию для данного типа. Она похожа на
Option::take
иCell::take
и является удобным сокращением дляmem::replace(&mut dst, Default::default())
. -
BTreeMap::get_key_value
иHashMap::get_key_value
Возвращает пару ключ-значение, соответствующие предоставленному ключу.
-
Option::as_deref
,Option::as_deref_mut
Они работают подобно
Option::as_ref
иOption::as_mut
, но используютDeref
иDerefMut
соответственно, таким образом,opt_box.as_deref()
иopt_box.as_deref_mut()
, гдеopt_box: Option<Box<T>>
, создаютOption<&T>
иOption<&mut T>
соответственно. -
Эта функция, подобно
Iterator::flatten
, разворачиваетOption<Option<T>>
вOption<T>
, производяSome(x)
дляSome(Some(x))
иNone
в противном случае. -
Возвращает адрес удалённого узла, к которому подключён сокет.
-
{f32,f64}::to_be_bytes
,{f32,f64}::to_le_bytes
,{f32,f64}::to_ne_bytes
,{f32,f64}::from_be_bytes
,{f32,f64}::from_le_bytes
и{f32,f64}::from_ne_bytes
Возвращают представление в памяти числа с плавающей точкой в виде массива байт с big-endian (network), little-endian или native-endian порядком байт.
Другие изменения
Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения.
Пожалуйста, прочтите заметки о совместимости, чтобы узнать, затронут ли вас эти изменения.
Участники 1.40.0
Множество людей собрались вместе, чтобы создать Rust 1.40.0. Мы не смогли бы сделать это без всех вас, спасибо!
От переводчиков
С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов.
Данную статью совместными усилиями перевели andreevlex, blandger, funkill, Hippolot, P0lunin, PsyHaSTe и LooMaclin.
Автор: RustLangRu