Русский
Русский
English
Статистика
Реклама

Перевод Rust 1.45.0 стабилизация функциональных процедурных макросов, исправление дефектов преобразования

Команда 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 были стабилизированы следующие функции:



Также теперь можно использовать char с диапазонами для итерации по символам:


for ch in 'a'..='z' {    print!("{}", ch);}println!();// Выведет "abcdefghijklmnopqrstuvwxyz"

Полный список изменений вы можете увидеть в детальных примечаниях к выпуску.


Другие изменения


Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения.


Участники 1.45.0


Множество людей собрались вместе, чтобы создать Rust 1.45.0. Мы не смогли бы сделать это без всех вас, спасибо!


От переводчиков


С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.


Данную статью совместными усилиями перевели nlinker, funkill, Hirrolot и blandger.

Источник: habr.com
К списку статей
Опубликовано: 18.07.2020 18:23:19
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Open source

Rust

Компиляторы

Программирование

Системное программирование

Clippy

Cargo

Rustc

Rustdoc

Rustfmt

Rustfix

Release

Stable

Языки программирования

Стабильная версия

Выпуск версий

Новости технологий

Перевод

Категории

Последние комментарии

© 2006-2020, personeltest.ru