Команда Rust рада сообщить о выпуске новой версии, 1.45.0. Rust это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.
Если вы установили предыдущую версию Rust средствами
rustup
, то для обновления до версии 1.45.0 вам
достаточно выполнить следующую команду:
rustup update stable
Если у вас ещё не установлен rustup
, вы можете
установить его с соответствующей страницы
нашего веб-сайта, а также посмотреть на
GitHub.
Что вошло в стабильную версию 1.45.0
Данный выпуск содержит два больших изменения: исправление давних дефектов при преобразовании между целыми числами и числами с плавающей точкой и стабилизация фич, необходимых для того, чтобы как минимум один веб-фреймворк смог работать на стабильном Rust.
Исправление дефектов в преобразованиях
Изначально Issue 10184 была открыта в
октябре 2013 года, за полтора года до выпуска Rust 1.0. Так как
rustc
использует LLVM в качестве
backend-компилятора, когда вы пишете подобный код:
pub fn cast(x: f32) -> u8 { x as u8}
компилятор Rust в версиях 1.44.0 и раньше генерировал следующее LLVM-IR:
define i8 @_ZN10playground4cast17h1bdf307357423fcfE(float %x) unnamed_addr #0 {start: %0 = fptoui float %x to i8 ret i8 %0}
fptoui
реализует преобразование и является
сокращением от "floating point to unsigned integer".
Но здесь есть проблема, описанная в документации:
Инструкция fptoui преобразовывает операнд с плавающей точкой в ближайшее (округляя до нуля) беззнаковое целое значение. Если значение не помещается вty2
, то результирующее значение будет испорченным.ОригиналThe fptoui instruction converts its floating-point operand into the nearest (rounding towards zero) unsigned integer value. If the value cannot fit in ty2, the result is a poison value.
Следующая часть, если только вы регулярно не копаетесь в недрах компиляторов, может быть не совсем понятна. Она полна жаргона, но есть более простое объяснение: если вы приводите большое число с плавающей запятой к маленькому целому числу, вы получаете неопределённое поведение.
Это означает что, например, поведение следующего кода не определено:
fn cast(x: f32) -> u8 { x as u8}fn main() { let f = 300.0; let x = cast(f); println!("x: {}", x);}
На моём компьютере с Rust 1.44.0 этот код печатает "x: 0", но
т.к. его поведение не определено, напечатать он может всё что
угодно. Это мы называем ошибкой корректности (ведь
unsafe
кода тут нет) то есть ошибка, когда компилятор
делает неправильные вещи. Мы отмечаем их в нашем трекере как
I-unsound, и относимся к ним очень
серьёзно.
Однако эта ошибка заняла много времени для исправления. Причина в том, что было очень неясно, каким должен быть правильный путь.
В итоге было принято решение сделать так:
-
as
будет выполнять "насыщающее приведение" (saturating cast), - будет добавлено новое
unsafe
приведение, если вы хотите пропустить проверки.
Это очень похоже на доступ к массиву, например:
-
array[i]
проверит, чтобы убедиться, чтоarray
содержит по крайней мереi + 1
элемент, - можно использовать
unsafe { array.get_unchecked(i) }
, чтобы пропустить проверку.
Итак, что такое насыщающее приведение? Давайте посмотрим на слегка изменённый пример:
fn cast(x: f32) -> u8 { x as u8}fn main() { let too_big = 300.0; let too_small = -100.0; let nan = f32::NAN; println!("too_big_casted = {}", cast(too_big)); println!("too_small_casted = {}", cast(too_small)); println!("not_a_number_casted = {}", cast(nan));}
Выведет:
too_big_casted = 255too_small_casted = 0not_a_number_casted = 0
То есть слишком большие числа превращаются в максимально возможное значение. Слишком малые числа дают наименьшее возможное значение (равное нулю). NaN выдаёт ноль.
А это новый API для небезопасного приведения:
let x: f32 = 1.0;let y: u8 = unsafe { x.to_int_unchecked() };
Но, как всегда, использовать этот метод рекомендуется лишь в крайних случаях. Как и при доступе к массиву, компилятор часто может соптимизировать проверки, делая эквивалентными безопасные и небезопасные версии, где это возможно доказать.
Стабилизация функциональных процедурных макросов в выражениях, шаблонах и стейтментах
В Rust 1.30.0 мы стабилизировали
функциональные процедурные макросы в позиции элемента. Например,
крейт gnome-class
:
Gnome-класс это процедурный макрос для Rust. Внутри макроса мы определяем Rust-о подобный мини-язык, имеющий расширения, позволяющие вам определять подклассы GObject, их свойства, сигналы, реализации интерфейса и остальные функции GObject. Цель состоит в том, чтобы не требовать небезопасного кода с вашей стороны.
Это выглядит так:
gobject_gen! { class MyClass: GObject { foo: Cell<i32>, bar: RefCell<String>, } impl MyClass { virtual fn my_virtual_method(&self, x: i32) { ... do something with x ... } }}
В "позиции элемента" это некий жаргон, но в основном это
означает, что вы можете вызывать только gobject_gen!
в
определённых местах в вашего кода.
Rust 1.45.0 добавляет возможность вызывать процедурные макросы в трёх новых местах:
// представим, что мы имеем процедурный макрос "mac"mac!(); // позиция элемента, то, что было стабилизировано ранее// но здесь представлены 3 новых:fn main() { let expr = mac!(); // в выражении match expr { mac!() => {} // в шаблоне } mac!(); // в стейтменте}
Возможность использовать макросы в большем количестве мест интересна, но есть ещё одна причина, по которой многие разработчики давно ждали эту функцию: Rocket. Популярный веб-фреймворк Rocket, первоначально выпущенный в декабре 2016 года, часто называют одной из лучших вещей, которую может предложить экосистема Rust. Вот пример "Привет, мир" из его предстоящего релиза:
#[macro_use] extern crate rocket;#[get("/<name>/<age>")]fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name)}#[launch]fn rocket() -> rocket::Rocket { rocket::ignite().mount("/hello", routes![hello])}
До этого дня Rocket зависела от функциональности из ночной
версии компилятора для предоставления своей гибкости и эргономики.
По факту, как можно видеть на домашней странице
проекта, тот же пример что выше в текущей версии Rocket требует
наличия свойства proc_macro_hygiene
для компиляции.
Тем не менее, как вы можете догадаться из названия свойства,
сегодня оно попадёт стабильный выпуск! Данная
проблема для отслеживания истории ночных функций в Rocket.
Теперь они все проверены и готовы к использованию!
Следующая версия Rocket всё ещё находится в разработке, но когда она выйдет, многие будут очень довольны :)
Изменения в стандартной библиотеке
В Rust 1.45.0 были стабилизированы следующие функции:
-
Arc::as_ptr
, -
BTreeMap::remove_entry
, -
Rc::as_ptr
, -
rc::Weak::as_ptr
, -
rc::Weak::from_raw
, -
rc::Weak::into_raw
, -
sync::Weak::as_ptr
, -
sync::Weak::from_raw
, -
sync::Weak::into_raw
, -
str::strip_prefix
, -
str::strip_suffix
, -
char::UNICODE_VERSION
, -
Span::resolved_at
, -
Span::located_at
, -
Span::mixed_site
, -
unix::process::CommandExt::arg0
.
Также теперь можно использовать
char
с диапазонами для итерации по символам:
for ch in 'a'..='z' { print!("{}", ch);}println!();// Выведет "abcdefghijklmnopqrstuvwxyz"
Полный список изменений вы можете увидеть в детальных примечаниях к выпуску.
Другие изменения
Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения.
Участники 1.45.0
Множество людей собрались вместе, чтобы создать Rust 1.45.0. Мы не смогли бы сделать это без всех вас, спасибо!
От переводчиков
С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.
Данную статью совместными усилиями перевели nlinker, funkill, Hirrolot и blandger.