Выпуск Rust 1.30
Команда разработчиков Rust рада сообщить о выпуске новой версии Rust: 1.30.0. Rust — это системный язык программирования, нацеленный на безопасность, скорость и параллельное выполнение кода.
Если у вас установлена предыдущая версия Rust с помощью rustup, то для обновления
Rust до версии 1.30.0 вам достаточно выполнить:
| 1 | $ rustup update stable
 | 
Если у вас ещё не установлен rustup, вы можете установить его с соответствующей
страницы нашего веб-сайта. С подробными примечаниями к выпуску Rust 1.30.0
можно ознакомиться на GitHub.
Что вошло в стабильную версию 1.30.0
Rust 1.30 — выдающийся выпуск с рядом важных нововведений. Но уже в понедельник в официальном блоге будет опубликована просьба проверить бета-версию Rust 1.31, которая станет первым релизом «Rust 2018». Дополнительную информацию об этом вы найдёте в нашей предыдущей публикации «What is Rust 2018».
Процедурные макросы
Ещё в Rust 1.15 мы добавили возможность определять «пользовательские derive-макросы».
Например, с помощью serde_derive, вы можете объявить:
| 1 2 3 4 | #[derive(Serialize, Deserialize, Debug)] struct Pet { name: String, } | 
И конвертировать Pet в JSON и обратно в структуру, используя serde_json. Это возможно
благодаря автоматическому выводу типажей Serialize и Deserialize с помощью процедурных
макросов в serde_derive.
Rust 1.30 расширяет функционал процедурных макросов, добавляя возможность определять ещё два других типа макросов: «атрибутные процедурные макросы» и «функциональные процедурные макросы».
Атрибутные макросы подобны derive-макросам для автоматического вывода, но вместо генерации
кода только для атрибута #[derive], они позволяют пользователям создавать собственные новые
атрибуты. Это делает их более гибкими: derive-макросы работают только для структур и
перечислений, но атрибуты могут применяться и к другим объектам, таким как функции. Например,
атрибутные макросы позволят вам при использовании веб-фреймворка делать следующее:
| 1 2 | #[route(GET, "/")]
fn index() {
 | 
Этот атрибут #[route] будет определён в самом фреймворке как процедурный макрос. Его
сигнатура будет выглядеть так:
| 1 2 | #[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
 | 
Здесь у нас имеется два входных параметра типа TokenStream: первый — для содержимого
самого атрибута, то есть это параметры GET, "/". Второй — это тело того объекта, к
которому применён атрибут. В нашем случае — это fn index() {} и остальная часть
тела функции.
Функциональные макросы определяют такие макросы, использование которых выглядит как
вызов функции. Например, макрос sql!:
| 1 | let sql = sql!(SELECT * FROM posts WHERE id=1); | 
Этот макрос внутри себя будет разбирать SQL-выражения и проверять их на синтаксическую корректность. Подобный макрос должен быть объявлен следующим образом:
| 1 2 | #[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
 | 
Это похоже на сигнатуру derive-макроса: мы получаем токены, которые находятся внутри скобок, и возвращаем сгенерированный по ним код.
Макросы и use
Теперь можно импортировать макросы в область видимости с помощью ключевого слова use.
Например, для использования макроса json из пакета serde-json, раньше использовалась запись:
| 1 2 3 4 5 6 7 8 9 10 11 | #[macro_use] extern crate serde_json; let john = json!({ "name": "John Doe", "age": 43, "phones": [ "+44 1234567", "+44 2345678" ] }); | 
А теперь вы должны будете написать:
| 1 2 3 4 5 6 7 8 9 10 11 12 | extern crate serde_json; use serde_json::json; let john = json!({ "name": "John Doe", "age": 43, "phones": [ "+44 1234567", "+44 2345678" ] }); | 
Здесь макрос импортируется также, как и другие элементы, так что нет
необходимости в использовании аннотации macro_use.
Наконец, стабилизирован пакет proc_macro,
который даёт API, необходимый для написания процедурных макросов. В нем также значительно
улучшили API для обработки ошибок, и такие пакеты, как syn и quote уже используют его.
Например, раньше:
#[derive(Serialize)]
struct Demo {
    ok: String,
    bad: std::thread::Thread,
}
приводило к такой ошибке:
| 1 2 3 4 5 | error[E0277]: the trait bound `std::thread::Thread: _IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not satisfied --> src/main.rs:3:10 | 3 | #[derive(Serialize)] | ^^^^^^^^^ the trait `_IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not implemented for `std::thread::Thread` | 
Теперь же будет выдано:
| 1 2 3 4 5 | error[E0277]: the trait bound `std::thread::Thread: serde::Serialize` is not satisfied --> src/main.rs:7:5 | 7 | bad: std::thread::Thread, | ^^^ the trait `serde::Serialize` is not implemented for `std::thread::Thread` | 
Улучшение системы модулей
Система модулей долгое время становилась больным местом для новичков в Rust’е; некоторые из её правил оказывались неудобными на практике. Настоящие изменения являются первым шагом, который мы предпринимаем на пути упрощения системы модулей.
В дополнении к вышеупомянутому изменению для макросов, есть два новых улучшения
в использовании use. Во-первых, внешние пакеты теперь добавляются в prelude,
то есть:
| 1 2 3 4 5 | // было let json = ::serde_json::from_str("..."); // стало let json = serde_json::from_str("..."); | 
Подвох в том, что старый стиль не всегда был нужен из-за особенностей работы системы модулей Rust:
extern crate serde_json;
fn main() {
    // это прекрасно работает; мы находимся в корне пакета, поэтому `serde_json`
    // здесь в области видимости
    let json = serde_json::from_str("...");
}
mod foo {
    fn bar() {
        // это не работает; мы внутри пространства имён `foo`, и `serde_json`
        // здесь не объявлен
        let json = serde_json::from_str("...");
    }
    // одно решение - это импортировать его внутрь модуля с помощью `use`
    use serde_json;
    fn baz() {
        // другое решение - это использовать `::serde_json`, когда указывается
        // абсолютный путь, вместо относительного
        let json = ::serde_json::from_str("...");
    }
}
Было неприятно получать сломанный код, просто перемещая функцию в подмодуль.
Теперь же будет проверяться первая часть пути, и если она соответствует некоторому
extern crate, то он будет использоваться независимо от положения вызова в иерархии
модулей.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | mod foo { pub fn bar() { // ... } } // было use ::foo::bar; // или use foo::bar; // стало use crate::foo::bar; | 
Ключевое слово crate в начале пути указывает, что путь будет начинаться от корня пакета.
Раньше пути, указанные в строке импорта use, всегда указывались относительно корня
пакета, но пути в остальном коде, напрямую ссылающиеся на элементы, указывались относительно
текущего модуля, что приводило к противоречивому поведению путей:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | mod foo { pub fn bar() { // ... } } mod baz { pub fn qux() { // было ::foo::bar(); // не работает, в отличии от `use`: // foo::bar(); // стало crate::foo::bar(); } } | 
Как только новый стиль станет широко использоваться, то он, мы надеемся, сделает абсолютные
пути более ясными, без необходимости использовать уродливый префикс ::.
Все эти изменения в совокупности упрощают понимание того, как разрешаются пути. В любом
месте, где вы видите путь a::b::c, кроме оператора use, вы можете спросить:
- Является ли aименем пакета? Тогда нужно искатьb::cвнутри него.
- Является ли aключевым словомcrate? Тогда нужно искатьb::cот корня текущего пакета.
- В противном случае, нужно искать a::b::cот текущего положения в иерархии модулей.
Старое поведение путей в use, всегда начинающихся от корня пакета, по-прежнему применимо.
Но при единовременном переходе на новый стиль, данные правила будут применяться к путям повсюду
единообразно, и вам придётся гораздо меньше заботиться об импортах при перемещении кода.
Сырые идентификаторы
Вы можете теперь использовать ключевые слова как идентификаторы, используя следующий новый синтаксис:
| 1 2 3 4 5 6 7 8 9 10 | // определение локальной переменной с именем `for` let r#for = true; // определение функции с именем `for` fn r#for() { // ... } // вызов той функции r#for(); | 
Пока не так много случаев, когда вам это пригодится. Но однажды вы попытаетесь использовать пакет для Rust 2015 в проекте для Rust 2018 или наоборот, тогда набор ключевых слов у них будет разным. Мы расскажем об этом подробнее в предстоящем анонсе Rust 2018.
Приложения без стандартной библиотеки
Ещё в Rust 1.6 мы объявили о стабилизации no_std и libcore
для создания проектов без стандартной библиотеки. Однако, с одним уточнением:
можно было создавать только библиотеки, но не приложения.
В Rust 1.30 можно использовать атрибут #[panic_handler]
для самостоятельной реализации паники. Это означает, что теперь можно создавать
приложения, а не только библиотеки, которые не используют стандартную библиотеку.
Другое
И последнее: в макросах теперь можно
сопоставлять модификаторы области видимости, такие как pub,
с помощью спецификатора vis. Дополнительно, «инструментальные
атрибуты», такие как #[rustfmt::skip], теперь стабилизированы.
Правда для использования с инструментами статического анализа, наподобие
#[allow(clippy::something)], они ещё не стабильны.
Подробности смотрите в примечаниях к выпуску.
Стабилизация стандартной библиотеки
В этом выпуске были стабилизированы следующие API:
- Ipv4Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
- Ipv6Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
- Iterator::find_map
Кроме того, стандартная библиотека уже давно имеет функции для удаления пробелов с
одной стороны некоторого текста, такие как trim_left. Однако, для RTL-языков значение
«справа» и «слева» тут приводят к путанице. Поэтому мы вводим новые имена для этих функций:
- trim_left->- trim_start
- trim_right->- trim_end
- trim_left_matches->- trim_start_matches
- trim_right_matches->- trim_end_matches
Мы планируем объявить устаревшими старые имена (но не удалить, конечно) в Rust 1.33.
Подробности смотрите в примечаниях к выпуску.
Улучшения в Cargo
Самое большое улучшение Cargo в этом выпуске заключается в том, что теперь у нас есть индикатор выполнения!

Подробности смотрите в примечаниях к выпуску.
Разработчики 1.30.0
Множество людей совместно создавало Rust 1.30. Мы не смогли бы завершить работу без участия каждого из вас. Спасибо!
