Представляю Вашему вниманию перевод публикации о новой версии всеми любимого языка программирования Rust.
Введение
Команда по разработке языка программирования Rust рада анонсировать новую версию, 1.35.0. Rust — это язык программирования, позволяющий каждому разрабатывать надёжное и быстрое ПО.
Если предыдущую версию Rust Вы установили средствами rustup
, получение текущей версии не составит особого труда:
$ rustup update stable
Если у Вас всё ещё нет rustup
, Вы можете получить его с соответствующей страницы на нашем сайте. Детальный обзор данного релиза доступен на GitHub.
Что вошло в стабильную версию?
Главными новшествами этого релиза можно назвать реализации трейтов FnOnce
, FnMut
и Fn
на структурах Box<dyn FnOnce>
, Box<dyn FnMut>
и Box<dyn Fn>
соответственно.
А также встраиваемые функции (closures) могут быть сконвертированы в небезопасные указатели на функции. Макрос dbg!
, введённый в Rust 1.32.0, теперь может быть вызван без указания аргументов.
Более того, данный релиз внёс множество стабилизаций стандартной библиотеки. Ниже представлены наиболее существенные, но доступен и детальный разбор оных.
Fn*
трейты реализованы на Box<dyn Fn*>
В Rust 1.35.0, трейты FnOnce
, FnMut
и Fn
реализованы на Box<dyn FnOnce>
, Box<dyn FnMut>
и Box<dyn Fn>
соответственно.
В прошлом, если Вы желали вызвать функцию, инкапсулированную в Box
, Вам необходимо было воспользоваться FnBox
, так как объекты Box<dyn FnOnce>
и подобные не реализовывали соответствующие трейты Fn*
. Это также мешало передаче инкапсулированных в Box
функций коду, ожидавшему реализатор трейта Fn
(предлагалось создавать временные встраиваемые функции).
Это было вызвано неспобностью компилятора обнаруживать подобные реализации. Этот недостаток был устранён с введением unsized_locals
.
Однако теперь Вы можете использовать инкапсулированные в Box
функции даже в тех местах, которые ожидают реализацию функционального трейта. Например, приведённый ниже код компилируется без ошибок:
fn foo(x: Box<dyn Fn(u8) -> u8>) -> Vec<u8> {
vec![1, 2, 3, 4].into_iter().map(x).collect()
}
Объекты Box<dyn FnOnce>
могут быть вызваны без лишней возни:
fn foo(x: Box<dyn FnOnce()>) {
x()
}
Конвертация в небезопасные указатели
Со времен Rust 1.19.0 стало возможным конвертирование встраиваемых функций, не захватывавших среду в указатели на функции. Например, вы могли написать:
fn twice(x: u8, f: fn(u8) -> u8) -> u8 {
f(f(x))
}
fn main() {
assert_eq!(42, twice(0, |x| x + 21));
}
Но к сожалению, данная возможность не была расширена до небезопасных указателей на функции. Данный релиз привнёс описанные выше изменения:
/// Безопасные инварианты, переданные в `unsafe fn`.
unsafe fn call_unsafe_fn_ptr(f: unsafe fn()) {
f()
}
fn main() {
// БЕЗОПАСНОСТЬ: тут нет никаких инвариантов
// Данная функция статически предотвращена от небезопасных
// операций
unsafe {
call_unsafe_fn_ptr(|| {
dbg!();
});
}
}
Вызов dbg!()
без аргументов
Вследствие обилия вызовов println!
в качестве колхозных дебаггеров, в Rust 1.32.0 был представлен макрос dbg!
. Напомним, что данный макрос позволяет быстро запечатлить результат некого выражения с контекстом:
fn main() {
let mut x = 0;
if dbg!(x == 1) {
x += 1;
}
dbg!(x);
}
Приведённые выше строки кода напечатают в терминал результат выражения x == 1
и x
соответственно:
[src/main.rs:4] x == 1 = false
[src/main.rs:8] x = 0
Как было сказано в предыдущей секции, где может быть вызвана функция высшего порядка call_unsafe_fn_ptr
, dbg!()
также подлежит вызову без указания аргументов. Это может быть крайне полезно для обнаружения выбранных программных веток:
fn main() {
let condition = true;
if condition {
dbg!(); // [src/main.rs:5]
}
}
Стабилизации стандартной библиотеки
В Rust 1.35.0, множество компонентов стандартной библиотеки были стабилизированы. В дополнение к этому, были внесены некоторые реализации, о которых Вы можете прочесть тут.
Копирование знака числа с плавающей точкой в другое число
С данным релизом, новые методы copysign
были добавлены в примитивы с плавающей точкой (конкретнее, f32
и f64
):
Как можно было предположить из названия методов, Вы можете использовать их для копирования знака одного числа в другое:
fn main() {
assert_eq!(3.5_f32.copysign(-0.42), -3.5);
}
Проверка того, содержит ли Range
определённое значение
Rust 1.35.0 приобрёл парочку новых методов на структурах Range*
:
Range::contains
RangeFrom::contains
RangeTo::contains
RangeInclusive::contains
RangeToInclusive::contains
С помощью данных методов, Вы можете с лёгкостью проверить, находится ли определённое значение в диапазоне. Например, Вы можете написать:
fn main() {
if (0..=10).contains(&5) {
println!("5 находится в диапазоне [0; 10].");
}
}
Перевести (map) и разбить (split) заимствованный RefCell
С приходом Rust 1.35.0, Вы можете перевести и разбить заимствованное значение RefCell
на множество заимствованных значений на разные компоненты заимствованных данных:
Переставить значение RefCell
через встраиваемую функцию
Этот релиз представляет удобный метод replace_with
, объявленный на структуре RefCell
:
Хешировать указатель или ссылку по адресу
Данный релиз представляет функцию ptr::hash
, принимающую сырой указатель для хеширования. Использование ptr::hash
способно предотвратить хеширование указываемого или ссылаемого значения вместо самого адреса.
Копирование контента Option<&T>
С началом Rust 1.0.0, методы Option::cloned
на Option<&T>
и Option<&mut T>
позволяли клонировать содержание в случае его присутствия (Some(_)
). Однако клонирование иногда может быть дорогостоящей операцией, а методы opt.cloned()
не описывали никаких подсказок.
Этот релиз внёс:
Option::copied
дляOption<&T>
иOption<&mut T>
Функциональность opt.copied()
такая же, как и opt.cloned()
. Однако описанный выше метод запрашивает условия T: Copy
, невыполнение которого вызвет ошибку компиляции.
Изменения в Clippy
Clippy, инструмент, отлавливающий часто встречаемые несовершенности для повышения качества кода, обзавёлся drop_bounds
. Оно срабатывает в тех случаях, когда обобщённая функция запрашивает выполнения условия T: Drop
:
fn foo<T: Drop>(x: T) {}
Это часто является ошибкой, так как примитивы не реализуют Drop
. Более того, T: Drop
не покрывает типы, подобные String
, не имеющие наглядного поведения деструктора, а скорее результат встраиваемых типов (как Vec<u8>
).
В дополнении к drop_bounds
, данный релиз разделяет redundant_closure
в redundant_closure
и redundant_closure_for_method_calls
.
Прочесть детальный релиз Clippy можно тут.
Изменения в Cargo
Детальное описание изменений Cargo доступно здесь.
Участники 1.35.0
Множество людей собрались вместе чтобы создать Rust 1.35.0. Мы не смогли бы сделать это без всех Вас, спасибо!
От переводчика
С любыми вопросами по языку Rust Вам смогут помочь в русскоязычном Телеграм-чате, или же в аналогичном чате для новичковых вопросов.
Комментарии (13)
blandger
24.05.2019 16:55+2Наверное зря не сделали ссылку на русскую ТГ группу ??
orcy
24.05.2019 20:19+2fn foo(x: Box<dyn FnOnce()>) { x() }
А раньше как надо было? (*x)()?Gymmasssorla Автор
25.05.2019 07:38+1(*x)()
— это сишный синтаксис. Раньше надо былоFnBox
пользоваться, или же помещать функциональный трейт вBox
(что не очень удобно).orcy
25.05.2019 20:04+1Вроде *x для того чтобы достать что-то из бокса — это и растовый синтаксис.
То есть раньше если у вас оказался
Box<dyn FnOnce()>
то не было способа вызвать функцию? Это как-то непонятно.AntonZelenin
26.05.2019 15:24Если я не ошибаюсь, то было так:
To call a FnOnce closure that is stored in a Box, the closure needs to move itself out of the Box because the closure takes ownership of self when we call it. Rust doesn’t allow us to move a value out of a Box because Rust doesn’t know how big the value inside the Box will be.
Поэтому нужно было имплементить трейт
trait FnBox { fn call_box(self: Box<Self>); }
который «is allowed to take ownership of a Self value stored in a Box» и вызывать замыкание такx.call_box()
Подробнее можно почитать здесь: doc.rust-lang.org/book/ch20-02-multithreaded.html#implementing-the-execute-method
А теперь можно просто вызывать x() без всяких заморочек
ozkriff
Выпуск полезный, не сильно значительный — в основном мелкие эргономические улучшения. Ждем 1.36 с hashbrown и 1.37 с async'ом (какой бы там синтаксис не выбрали в итоге). :)
Тем временем, я пару недель назад таки срезал v0.5 версию своей пошаговой ржавой игры Zemeroth и написал огромную новость об этом в девлоге: "Zemeroth v0.5: ggez, WASM, itch.io, visuals, AI, campaign, tests".
Неприятные вопросы переезда между движками и работы в вебе хотя бы на время закрыты, роадмап написан, теперь планомерно движусь к выпуску v0.6.
humbug
А есть даты по parking_lot?
ozkriff
Там же еще даже PR не влит — https://github.com/rust-lang/rust/pull/56410 — раньше 1.37 точно не доберется.
Propp
Вроде как выбрали
.await
и даже слили PR.Halt
Они сами писали что это еще не окончательное решение. То есть они теоретически готовы к дискуссии, если в последний момент появится какая-то радикальная альтренатива, но вероятность этого довольно мала.