Команда разработчиков Rust рада сообщить о выпуске новой версии Rust, 1.31.0, а также "Rust 2018". Rust — это язык программирования, который дает возможность каждому создавать надежное и эффективное программное обеспечение.


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


$ rustup update stable

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


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


Rust 1.31, возможно, самый значительный выпуск со времен Rust 1.0! В данный выпуск включена первая итерация «Rust 2018», но это не единственное нововведение! Обзор улучшений будет длинный, поэтому вот оглавление:


  • Rust 2018
    • Нелексические времена жизни
    • Изменения системы модулей
  • Дополнительные правила вывода времен жизни
  • const fn
  • Новые инструменты
  • Инструментальные проверки качества кода
  • Документация
  • Предметные рабочие группы
  • Новый веб-сайт
  • Стабилизация стандартной библиотеки
  • Улучшения в Cargo
  • Разработчики выпуска

Rust 2018


Мы писали о Rust 2018 впервые в марте, и затем в июле. За подробностями зачем нужен Rust 2018, обратитесь к этим публикациям. В данном обзоре нам и так много что нужно рассказать, поэтому мы сосредоточимся только на том, что такое Rust 2018. Также об этом можно почитать в посте на Mozilla Hacks (перевод).


Вкратце, Rust 2018 — это возможность объединить в связное целое всю работу, которую мы проделали за последние три года. Rust 2018 — это нечто большее, чем просто набор улучшений языка. В дополнении к ним, он включает:


  • Инструментарий (поддержка в IDE, rustfmt, Clippy)
  • Документацию
  • Работу предметных рабочих групп
  • Новый веб-сайт

Дальше мы расскажем обо всем этом подробнее и о других нововведениях.


Давайте создадим новый проект с помощью Cargo:


$ cargo new foo

Вот содержимое Cargo.toml:


[package]
name = "foo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

В секцию [package] был добавлен новый ключ: edition. Обратите внимание, что он установлен в 2018. Вы также можете установить его в 2015 — это значение будет установлено по умолчанию, если ключ отсутствует.


С использованием Rust 2018 будут разблокированы некоторые новые возможности, которые не разрешены в Rust 2015.


Важно отметить, что каждый пакет может быть в режиме 2015 или 2018, и они будут работать вместе. Ваш проект 2018 редакции может использовать зависимости 2015 редакции, а проект 2015 редакции может использовать зависимости 2018 редакции. Это гарантирует целостность экосистемы и что все новые возможности будут опциональны, сохраняя совместимость с существующим кодом. Кроме того, когда вы решите перенести код Rust 2015 на Rust 2018, изменения могут быть внесены автоматически через cargo fix.


Вы можете спросить: а что с самими новыми возможностями? Во-первых, они добавляются также и в Rust 2015, если оказываются совместимы с особенностями этой редакции. Таким образом, большая часть языка остается одинаковой везде. Вы можете посмотреть руководство по редакциям, чтобы узнать минимальную версию rustc для каждой новой возможности и другие ее требования. Однако, есть несколько больших нововведений, о которых нужно упомянуть отдельно: нелексические времена жизни и некоторые изменения в системе модулей.


Нелексические времена жизни


Если вы следили за развитием Rust последние несколько лет, то вы могли время от времени встречать термин "NLL" или "нелексические времена жизни" ("non-lexical lifetimes"). Это — жаргонизм, который, если ��оворить простыми словами, означает: анализатор заимствований стал умнее и теперь принимает некоторый корректный код, который до этого отклонял. Рассмотрим пример:


fn main() {
    let mut x = 5;

    let y = &x;

    let z = &mut x;
}

Раньше Rust выдавал ошибку компиляции:


error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:18
  |
4 |     let y = &x;
  |              - immutable borrow occurs here
5 |     
6 |     let z = &mut x;
  |                  ^ mutable borrow occurs here
7 | }
  | - immutable borrow ends here

Это потому, что область жизни ссылок определялась "лексически"; то есть, заимствование y считалось активным, пока y не выйдет из области видимости в конце main, даже если мы никогда больше не используем y внутри области. С кодом выше все в порядке, но анализатор зависимостей не мог этого понять.


Теперь этот код замечательно компилируется.


Но что, если бы мы использовали y? Например так:


fn main() {
    let mut x = 5;
    let y = &x;
    let z = &mut x;

    println!("y: {}", y);
}

Раньше Rust выдавал вам такую ошибку:


error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:18
  |
4 |     let y = &x;
  |              - immutable borrow occurs here
5 |     let z = &mut x;
  |                  ^ mutable borrow occurs here
...
8 | }
  | - immutable borrow ends here

В Rust 2018 это сообщение об ошибке улучшилось:


error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:13
  |
4 |     let y = &x;
  |             -- immutable borrow occurs here
5 |     let z = &mut x;
  |             ^^^^^^ mutable borrow occurs here
6 |     
7 |     println!("y: {}", y);
  |                       - borrow later used here

Вместо того, чтобы указывать, где y выходит из области видимости, оно показывает, где происходит конфликтное заимствование. Это значительно упрощает отладку ошибок такого рода.


В Rust 1.31 это улучшение исключительно для Rust 2018. Мы планируем добавить его в Rust 2015 позже.


Изменения системы модулей


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


Поэтому редакция 2018 вносит некоторые изменения в то, как работают пути, упрощая систему модулей и делая ее понятнее.


Вот краткое резюме:


  • extern crate больше не требуется практически нигде.
  • Вы можете импортировать макросы с помощью use, вместо использования атрибута #[macro_use].
  • Абсолютные пути начинаются с имени контейнера, где ключевое слово crate относится к текущему контейнеру.
  • foo.rs и поддиректория foo/ могут сосуществовать; mod.rs больше не требуется при размещении подмодулей в поддиректории.

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


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


Дополнительные правила вывода времен жизни


Давайте поговорим об улучшении, доступном в обоих редакциях: мы добавили некоторые дополнительные правила вывода для блоков impl и определений функций. Код вроде такого:


impl<'a> Reader for BufReader<'a> {
    // здесь методы
}

может быть теперь записан так:


impl Reader for BufReader<'_> {
    // здесь методы
}

Время жизни '_ по-прежнему показывает, чтоBufReader принимает его как параметр, но нам больше не нужно задавать для него имя.


Времена жизни все еще требуется определять в структурах. Однако нам больше не нужно писать столько шаблонного кода, как раньше:


// Rust 2015
struct Ref<'a, T: 'a> {
    field: &'a T
}

// Rust 2018
struct Ref<'a, T> {
    field: &'a T
}

Зависимость : 'a будет выведена. Вы все еще можете указать ее явно, если хотите. Мы рассматриваем и другие возможности для вывода в подобных местах на будущее, но пока не имеем конкретных планов.


const fn


В Rust есть несколько способов объявления функции: fn для обычных функций, unsafe fn для небезопасных функций и extern fn для внешних функций. В этом выпуске добавлен новый способ объявления функции: const fn. Он используется так:


const fn foo(x: i32) -> i32 {
    x + 1
}

Константная функция может вызываться как обычная функция, но помимо этого она может использоваться в любом константном контексте. При этом она выполнится во время компиляции, а не во время выполнения программы. Например:


const SIX: i32 = foo(5);

Функция foo выполнится во время компиляции и SIX примет значение 6.


Константные функции не могут делать все то, что могут делать нормальные функции: они должны иметь детерминированный результат. Это важно из соображений надежности. В текущем виде константные функции могут совершать минимальное подмножество операций. Вот некоторые примеры того, что вы можете в них делать:


  • Использовать целочисленные арифметические операции и операции сравнения
  • Использовать любые логические операции, кроме && и ||
  • Конструировать массивы, структуры, перечисления и кортежи
  • Вызывать другие константные функции
  • Обращаться по индексу в массивах и срезах
  • Обращаться к полям структур и кортежей
  • Использовать константы (но не статические значения, и даже не ссылки на них)
  • Использовать & и * ссылки
  • Приводить типы, исключая приведение сырого указателя к целочисленному значению

Мы будем расширять возможности константных функций, но приведенного выше набора уже достаточно, чтобы использовать const fn на практике.


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


Новые инструменты


Редакция 2018 знаменует начало нового уровня зрелости экосистемы инструментов Rust. Cargo, Rustdoc и Rustup были основными инструментами, начиная с версии 1.0; с редакцией 2018 приходит новое поколение инструментов, которыми теперь все могут пользоваться: Clippy, Rustfmt и поддержка IDE.


Статический анализатор кода clippy теперь доступен в стабильном Rust. Вы можете установить его через rustup component add clippy и запустить командой cargo clippy. Clippy теперь получил версию 1.0 и имеет такие же гарантии стабильности статических проверок, что и rustc. Новые проверки могут добавляться, или может расширяться функционал старых, но старые не могут быть удалены (могут быть только помечены как устаревшие). Это означает, что код, который компилируется с clippy, будет продолжать компилироваться с clippy (при условии, что для проверок не задано генерировать
ошибку через deny), но может выдавать новые предупреждения.


Rustfmt — это инструмент для форматирования кода в Rust. Автоматическое форматирование кода позволит вам сэкономить время, кроме того, оно приблизит ваш код к официальному стилю Rust. Вы можете установить его через rustup component add rustfmt и использовать командой cargo fmt.


Текущий выпуск включает Rustfmt 1.0. Отныне мы гарантируем обратную совместимость для Rustfmt: если вы отформатируете свой код сегодня, то форматирование не изменится в будущем (только для параметров по умолчанию). Обратная совместимость означает, что теперь практично запускать Rustfmt на вашем CI (используйте cargo fmt --check). Попробуйте это вместе с "форматированием при сохранении" в редакторе, и ваш рабочий процесс революционизируется.


Поддержка IDE — одна из самых востребованных возможностей инструментов для Rust. Сейчас имеется несколько решений высокого качества:



Работа по поддержке в IDE не закончена. В частности, автодополнение кода в редакторах, основанных на RLS, не на высоте. Однако, если вы в основном хотите поддержку типов, документации и "перехода к определению", то вы останетесь довольны.


Инструментальные проверки качества кода (tool lints)


В Rust 1.30 мы стабилизировали "инструментальные атрибуты", такие как #[rustfmt::skip]. В Rust 1.31 мы стабилизировали нечто подобное: "инструментальные проверки качества кода" ("tool lints"), такие как #[allow(clippy::bool_comparison)]. Это позволяет задавать пространства имен для проверок, чтобы стало понятнее, из каких инструментов они берутся.


Если вы ранее использовали проверки Clippy, вы можете выполнить миграцию следующим образом:


// было
#![cfg_attr(feature = "cargo-clippy", allow(bool_comparison))]

// стало
#![allow(clippy::bool_comparison)]

Вам больше не нужен cfg_attr! Вам также теперь будут выдаваться предупреждения, которые помогут перейти на использование нового стиля.


Документация


В Rustdoc было несколько улучшений в этом году, и была выпущена полностью переписанная книга "The Rust Programming Language". Вы можете купить бумажную копию от No Starch Press!


Раньше ее называли "вторым изда��ием" книги, но, так как она стала первым печатным изданием, то это вызвало путаницу. Ведь и печатное издание планируется периодически обновлять. В конце концов, после множества дискуссий с No Starch, было решено обновлять книгу на веб-сайте вместе с каждым выпуском, а No Starch будет периодически забирать изменения и печатать их. Книга довольно хорошо продается и собирает деньги для Black Girls Code.


Вы можете найти новую версию книги здесь.


Предметные рабочие группы


В этом году мы объявили о создании четырех рабочих групп:


  • Сетевые сервисы
  • Приложения командной строки
  • WebAssembly
  • Встроенные устройства

Группы очень усердно работали, чтобы сделать Rust лучше в каждой из этих областей. Вот некоторые достижения:


  • В сетевых сервисах переработали интерфейс для Futures, а сверх этого еще и async/await. Эти улучшения еще не выпущены, но мы уже близки к этому!
  • Команда CLI работала над библиотеками и документацией, чтобы сделать приложения командной строки лучше.
  • Группа WebAssembly выпустила множество инструментов мирового уровня для использования Rust с wasm.
  • Для встроенных устройств появилась возможность вести разработку ARM на стабильном Rust!

Обо всем этом подробнее можно узнать на нашем новом сайте!


Новый веб-сайт


На прошлой неделе мы анонсировали новую версию нашего веб-сайта. Теперь она стала официальной версией rust-lang.org!


Для ее создания потребовался год работы многих людей. И хотя до полного завершения еще многое нужно сделать, мы гордимся проделанной работой.


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


Были добавлены новые реализации From:


  • u8 теперь реализует From<NonZeroU8>, аналогично и для других числовых типов и их NonZero эквивалентов
  • Option<&T> реализует From<&Option<T>>, аналогично и для &mut

Также были стабилизированы следующие функции:



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


Улучшения в Cargo


Cargo теперь будет загружать пакеты параллельно, используя HTTP/2.


Кроме того, так как extern crate указывать теперь не обязательно, было бы неприятно писать extern crate foo as bar; для переименования зависимости. Поэтому вы можете делать это в Cargo.toml таким образом:


[dependencies]
baz = { version = "0.1", package = "foo" }

или, что эквивалентно:


[dependencies.baz]
version = "0.1"
package = "foo"

Теперь пакет foo доступен как baz для использования в вашем коде.


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


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


Обычно в конце обзора мы благодарим людей, которые внесли свой вклад в подготовку выпуска. Но в этот раз, в отличии от прошлых, этот список не полностью охватывает всех тех людей, которые помогали, и все то количество работы, которая была проделана. Каждый обычный выпуск — это результат шестинедельной работы, но данный выпуск — это кульминация трех лет усилий, отраженных в несметном числе репозиториев, сделанных огромным количеством людей. Нам было приятно со всеми вами работать, и мы с нетерпением ожидаем продолжения развития Rust в течении следующих трех лет.


От переводчика: выражаю отдельную благодарность участникам сообщества Rustycrate и лично @dashadee, ozkriff, humbug и mvlabat за помощь с переводом и вычиткой.