Выпуск Rust 1.21

оригинал: The Rust Core Team • перевод: ozkriff • новости • поддержите на Patreon

Команда Rust рада представить выпуск Rust 1.21.0. Rust — это системный язык программирования, нацеленный на скорость, безопасность и параллельное выполнение кода.

Если у вас установлена предыдущая версия Rust, для обновления достаточно выполнить:

1
$ rustup update stable

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

Что вошло в стабильную версию 1.21.0

Этот выпуск содержит несколько небольших, но полезных изменений языка и новую документацию.

Первое изменение касается литералов. Рассмотрим код:

1
let x = &5;

В Rust он аналогичен следующему:

1
2
let _x = 5;
let x = &_x;

То есть 5 будет положено в стек или возможно в регистры, а x будет ссылкой на него.

Однако, учитывая, что речь идёт о целочисленном литерале, нет причин делать значение таким локальным. Представьте, что у нас есть функция, принимающая 'static аргумент вроде std::thread::spawn. Тогда вы бы могли использовать x так:

1
2
3
4
5
6
7
8
9
use std::thread;

fn main() {
    let x = &5;

    thread::spawn(move || {
        println!("{}", x);
    });
}

Этот код не соберётся в прошлых версиях Rust’а:

1
2
3
4
5
6
7
8
9
10
error[E0597]: borrowed value does not live long enough
  --> src/main.rs:4:14
   |
4  |     let x = &5;
   |              ^ does not live long enough
...
10 | }
   | - temporary value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

Из-за локальности 5, ссылка на него тоже живёт слишком мало, чтобы удовлетворить требованиям spawn.

Но если вы соберёте это с Rust 1.21, оно заработает. Почему? Если что-то, на что создана ссылка, можно положить в static, мы могли бы «обессахарить» let x = &5; в нечто вроде:

1
2
3
static FIVE: i32 = 5;

let x = &FIVE;

И раз FIVE является static, то x является &'static i32. Так Rust теперь и будет работать в подобных случаях. Подробности смотрите в RFC 1414, который был принят в январе 2017, но начинался ещё в декабре 2015!

Теперь мы запускаем LLVM параллельно с кодогенерацией, что должно снизить пиковое потребление памяти.

RLS теперь может быть установлен через rustup вызовом rustup component add rls-preview. Много полезных инструментов, таких как RLS, Clippy и rustfmt, все ещё требуют ночной Rust, но это первый шаг к их работе на стабильном канале. Ожидайте дальнейших улучшений в будущем, а пока взгляните на предварительную версию

Теперь об улучшениях документации. Первое: если вы посмотрите на документацию модуля std::os, содержащего функционал работы с операционными системами, вы увидите не только Linux — платформу, на которой документация была собрана. Нас долго расстраивало, что официальная документация была только для Linux. Это первый шаг к исправлению ситуации, хотя пока что это доступно только для стандартной библиотеки, а не для любого пакета (crate). Мы надеемся исправить это в будущем.

Далее, документация Cargo переезжает! Исторически документация Cargo была размещена на doc.crates.io, что не следовало модели выпусков (release train model), хотя сам Cargo следовал. Это приводило к ситуациям, когда какой-то функционал скоро должен был «влиться» в ночной Cargo, документация обновлялась, и в течение следующих 12 недель пользователи думали, что все работает, хотя это ещё не было правдой. https://doc.rust-lang.org/cargo будет новым домом для документации Cargo, хотя сейчас этот адрес просто перенаправляет на doc.crates.io. Будущие выпуски переместят настоящую документацию Cargo, и тогда уже doc.crates.io будет перенаправлять на doc.rust-lang.org/cargo. Документация Cargo уже давно нуждается в обновлении, так что ожидайте ещё новостей о ней в скором будущем!

Наконец, до этого выпуска у rustdoc не было документации. Теперь это исправлено: добавлена новая книга «rustdoc Book», доступная по адресу https://doc.rust-lang.org/rustdoc. Сейчас эта документация очень примитивна, но со временем она улучшится.

Подробности смотрите в примечаниях к выпуску.

Стабилизации в стандартной библиотеке

В этом выпуске не так много стабилизаций, но есть кое-что, очень упрощающее жизнь: из-за отсутствия обобщения относительно целых чисел (type-level integers), массивы поддерживали типажи только до размера 32. Теперь это исправлено для типажа Clone. Кстати, это же вызывало много ICE (внутренних ошибок компилятора), когда тип реализовывал только Copy, но не Clone. Для других типажей недавно был принят RFC об обобщении относительно целых чисел, который должен исправить ситуацию. Это изменение ещё не реализовано, но подготовительные работы уже ведутся.

Затем был стабилизирован Iterator::for_each, дующий возможность поглощать итератор ради побочных эффектов без for цикла:

1
2
3
4
5
6
7
// старый способ
for i in 0..10 {
    println!("{}", i);
}

// новый способ
(0..10).for_each(|i| println!("{}", i));

Какой из способов лучше зависит от ситуации. В коде выше for цикл прост, но, если вы выстраиваете цепочку итераторов, версия с for_each может быть понятнее:

1
2
3
4
5
6
7
8
9
10
// старый способ
for i in (0..100).map(|x| x + 1).filter(|x| x % 2 == 0) {
    println!("{}", i);
}

// новый способ
(0..100)
    .map(|x| x + 1)
    .filter(|x| x % 2 == 0)
    .for_each(|i| println!("{}", i));

Rc<T> и Arc<T> теперь реализуют From<&[T]> where T: Clone, From<str>, From<String>, From<Box<T>> where T: ?Sized, и From<Vec<T>>.

Стабилизированы функции max и min типажа Ord.

Стабилизирована встроенная функция (intrinsic) needs_drop.

Наконец, был стабилизирован std::mem::discriminant, позволяющий вам узнать активный вариант перечисления без использования match оператора.

Подробности смотрите в примечаниях к выпуску.

Функционал Cargo

Помимо вышеупомянутых изменений документации Cargo в этом выпуске получает большое обновление: [patch]. Разработанная в RFC 1969, секция [patch] вашего Cargo.toml может быть использована, когда вы хотите заменить части вашего графа зависимостей. Это можно было сделать и раньше, посредством [replace]. Если коротко, то [patch] это новый и более удобный [replace]. И хотя у нас нет планов убирать или объявлять устаревшим [replace], вам скорее всего стоит использовать именно [patch].

Как же работает [patch]? Допустим, у нас есть такой Cargo.toml:

1
2
[dependencies]
foo = "1.2.3"

Так же наш пакет (crate) foo зависит от пакета bar, и мы нашли ошибку в bar. Чтобы проверить это, мы скачиваем исходный код bar и обновляем наш Cargo.toml:

1
2
3
4
5
[dependencies]
foo = "1.2.3"

[patch.crates-io]
bar = { path = '/path/to/bar' }

Теперь при выполнении cargo build будет использована наша локальная версия bar, а не версия из crates.io, от которой на самом деле зависит foo.

Подробности смотрите в документации.

Также:

Подробности смотрите в примечаниях к выпуску.

Разработчики 1.21.0

Множество людей участвовало в разработке Rust 1.19. Мы не смогли бы этого добиться без участия каждого из вас. Спасибо!