Выпуск Rust 1.13
Команда Rust рада представить выпуск Rust 1.13.0. Rust — это системный язык программирования, нацеленный на безопасность, скорость и параллельное выполнение кода.
Как обычно, вы можете установить Rust 1.13.0 с соответствующей страницы официального сайта, а также ознакомиться с подробным списком изменений в 1.13.0 на GitHub. В этот выпуск вошло 1448 патчей.
Это была по-настоящему горячая пора в Rust. Мы участвовали в трёх конференциях подряд — RustConf, RustFest и Rust Belt Rust. Было классно увидеть так много любителей Rust; со многими мы встретились впервые! Мы много думали о будущем, разрабатывали план на 2017 и создавали инструменты, нужные нашим пользователям.
И несмотря на всё это, мы собрали новый выпуск с кучей новых крутых фишек.
Что вошло в стабильную версию 1.13
Выпуск 1.13 включает долгожданный оператор ?
, ускорение компиляции, добавление
некоторых возможностей в Cargo и стандартную библиотеку. Также этот выпуск привносит множество небольших улучшений в документацию и сообщения об ошибках. Это результат работы многих людей, и они не упоминаются в замечаниях к выпуску индивидуально.
Выпуск содержит важные исправления уязвимостей в Cargo. Он зависит от curl и OpenSSL,
, а они оба недавно опубликовали обновления безопасности. Подробнее смотрите соответствующие анонсы curl 7.51.0 и OpenSSL 1.0.2j.
Оператор ?
Rust приобрёл новый оператор ?
. Он делает работу c ошибками значительно приятнее,
убирая визуальный шум. Например, у нас есть такой код для чтения данных из файла:
fn read_username_from_file() -> Result {
let f = File::open("username.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
Код содержит два пути, которые могут завершиться ошибкой: открытие файла и чтение данных из него. Если в одном из них произойдёт сбой, нам нужно вернуть ошибку из функции read_username_from_file
. Для этого нам приходится match
ить результат операций ввода-вывода. Однако здесь мы просто пробрасываем ошибку по стеку вызовов, и использование match
— это постоянно повторяющийся шаблонный код. Его трудно читать.
С ?
вышеприведённый код будет выглядеть так:
fn read_username_from_file() -> Result {
let mut f = File::open("username.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
?
яляется сокращением для целого выражения match
, которое мы писали выше. Другими словами, ?
берёт Result
и, если он Ok
, разворачивает его и отдаёт вложенное значение. Если значение Result
— Err
, ?
возвращает управление из текущей функции. Это гораздо проще читать: вместо целого выражения мы используем символ »?». Так мы показываем,
что обрабатываем ошибку стандартным способом, передавая её вверх по стеку.
Опытные программисты на Rust могуть отметить, что это то же самое, что и макрос try!
,
доступный ещё с Rust 1.0
. Действительно, это то же самое. До 1.13 read_username_from_file
могла быть реализована так:
fn read_username_from_file() -> Result {
let mut f = try!(File::open("username.txt"));
let mut s = String::new();
try!(f.read_to_string(&mut s));
Ok(s)
}
Так зачем же вносить расширения в язык, если у нас уже есть макрос? По разным причинам.
Во-первых, try!
подтвердил свою исключительную полезность и часто используется в идиоматичном Rust. Он используется так часто, что заслуживает сладкого синтаксиса. Такого рода эволюция является одним из больших преимуществ мощной системы макросов: предполагаемые расширения синтаксиса языка могут быть прототипированы и протестированы без изменения самого языка. В свою очередь, макрос, превратившийся в исключительно полезный, может указывать на недостаток возможностей языка. Такая эволюция из try!
в ?
— отличный тому пример.
Одна из причин, по которым try!
нуждается в засахаривании — это то, что он довольно некрасив в случае многократного вызова в цепочке. Сравните:
try!(try!(try!(foo()).bar()).baz())
в противовес:
foo()?.bar()?.baz()?
Первый фрагмент достаточно сложно читать, и каждый слой обработки ошибок приписывает
в начало выражения вызов try!
. Это требует чрезмерной концентрации внимания для тривиального проброса ошибок, и затмевает основной рабочий код — вызовы foo
, bar
и baz
. Данный тип сцепления вызовов с обработкой ошибок характерен для ситуаций вроде шаблона проектирования builder
.
Наконец, специализированный синтаксис облегчит вывод ошибок — мы сможем учесть использование ?
. А для кода из макросов сложно сделать хорошие ошибки. Однако в данном выпуске сообщения об ошибках использования ?
реализованы ещё не полностью.
Хотя это небольшое улучшение, по нашему опыту ?
— стоящее улучшение эргономики try!
. Это хороший пример последовательного улучшения качества жизни, которые Rust будет получать и дальше.
Подробнее о ?
смотрите в RFC 243.
Улучшение производительности
Мы серьёзно сосредоточились на производительности компилятора. У нас уже есть хорошие новости, но в будущих выпусках будет ещё лучше.
Mark Simulacrum и Nick Cameron оттачивали perf.rust-lang.org, наш инструмент для отслеживания производительности компилятора. Он регулярно запускает rustc-benchmarks
на выделенном оборудовании и отслеживает изменения со временем. Этот инструмент записывает результаты каждого этапа компиляции и используется разработчиками, чтобы
сузить диапазон поиска коммитов, приведших к деградации производительности. Это важная часть нашего инструментария!
Мы можем воспользоваться им, чтобы взглянуть на график производительности
в период разработки 1.13 — с 16 августа по 29 сентября (график показан ниже). График начинается с 25 августа и отфильтрован по нескольким критериям — чтобы исключить недостоверные, неполные или противоречивые результаты. Можно заметить большие сокращения, которые количественно отражены на соответствующей странице статистики.
1 сентября произошло значительное улучшение — Niko включил нормализованные проекции кеша при трансляции. Это означает, что во время генерации LLVM IR компилятор больше не пересчитывает конкретные экземпляры ассоциированных типов в каждом месте их использования. Теперь он переиспользует ранее вычисленные значения. Эта оптимизация влияет не на любой код, но когда встречаются определённые типовые фрагменты, вы заметите разницу. Например, для futures-rs время сборки отладочной версии улучшилось на 40%.
Другая подобная оптимизация реализована Michael Woerister. Она ускоряет компиляцию контейнеров, которые экспортируют множество встраиваемых функций. Если функция помечена как #[inline]
, компилятор сохраняет её представление в MIR в rlib — помимо обычной трансляции для использования в текущем контейнере. Затем он транслирует её в каждом контейнере, который вызывает данную функцию. В ретроспективе, Michael сделал очевидную оптимизацию: в некоторых случаях встраиваемые функции предназначены только для других контейнеров, так что компилятор не должен транслировать их в контейнере, где они
объявлены. Разумеется, кроме случая, когда они там вызываются. Это экономит время на преобразование функции в LLVM IR и её обработку LLVM: оптимизацию и кодогенерацию.
В некоторых случаях это даёт впечатляющие результаты. Время сборки ndarray улучшилось на 50%, а для (неопубликованного) winapi 0.3, rustc теперь не производит машинный код совсем.
Но подождите, это ещё не всё! Nick Nethercote также обратил своё внимание на производительность компилятора, сконцентрировавшись на профилировнии и микрооптимизациях. Данный выпуск уже включает в себя некоторые плоды его трудов, другие запланированы на 1.14.
Подробнее смотрите замечания к выпуску.
Другие заметные изменения
Выпуск содержит важные исправления уязвимостей в Cargo. Он зависит от curl и OpenSSL, а они оба недавно опубликовали обновления безопасности. Подробнее смотрите соответствующие анонсы curl 7.51.0 и OpenSSL 1.0.2j.
Теперь вы можете использовать макросы на месте типа (RFC 873) и применять атрибуты к операторам (RFC 16):
// Используем макрос, чтобы назвать тип
macro_rules! Tuple {
{ $A:ty,$B:ty } => { ($A, $B) }
}
let x: Tuple!(i32, i32) = (1, 2);
// Применяем атрибут статического анализа к единственному оператору
#[allow(uppercase_variable)]
let BAD_STYLE = List::new();
Встроенные флаги сброса (англ. inline drop flags) удалены. Раньше компилятор хранил «флаг сброса» в структурах, чтобы понимать, исполнять ли деструктор, когда структуры перемещались в некоторых путях исполнения. Это увеличивало размер структур, что мешало передаче типов с деструкторами через границу FFI. Для кода, который не перемещает структуры только в части путей исполнения, эта память тратилась впустую. В 1.12 MIR стал транслятором по умолчанию — это было фундаментом многих улучшений, включая удаление этих встроенных флагов сброса. Теперь флаги сброса лежат в стеке тех функций, которым они нужны.
В 1.13 есть серьёзный баг в кодогенерации для ARM с аппаратной реализацией чисел с плавающей точкой. Это большинство платформ на базе ARM. На данный момент ARM — это платформа 2 уровня поддержки, поэтому этот баг не блокирует выпуск. Поскольку 1.13 содержит исправление безопасности, мы рекомендуем пользователям ARM использовать бета-версии 1.14. Эта ветка скоро получит исправление данной проблемы.
Стабилизация языковых возможностей
- Типаж
Reflect
объявлен устаревшим. Смотрите объяснение того, что это значит для параметричности (parametricity) в Rust. - Стабилизированы макросы на месте типа. RFC 873.
- Стабилизированы атрибуты на операторах. RFC 16.
Стабилизация библиотек
checked_abs
,wrapping_abs
иoverflowing_abs
RefCell::try_borrow
иRefCell::try_borrow_mut
Добавлены assert_ne!
иdebug_assert_ne!
Реализован AsRef<[T]>
дляstd::slice::Iter
Реализован CoerceUnsized
для{Cell, RefCell, UnsafeCell}
Реализован Debug
дляstd::path::{Components,Iter}
Реализованы типажи преобразования для char
SipHasher
устарел. ИспользуйтеDefaultHasher
.Реализовано больше типажей для std::io::ErrorKind
Возможности Cargo
cargo: В cargo добавлен флаг --all-features
cargo: Добавлен флаг --message-format
Подробнее смотрите замечания к выпуску.
Разработчики версии 1.13.0
155 человек внесли свой вклад в 1.13.0. Большое вам спасибо!
- Aaron Gallagher
- Abhishek Kumar
- aclarry
- Adam Medziński
- Ahmed Charles
- Aleksey Kladov
- Alexander von Gluck IV
- Alexandre Oliveira
- Alex Burka
- Alex Crichton
- Amanieu d’Antras
- Amit Levy
- Andrea Corradi
- Andre Bogus
- Andrew Cann
- Andrew Cantino
- Andrew Lygin
- Andrew Paseltiner
- Andy Russell
- Ariel Ben-Yehuda
- arthurprs
- Ashley Williams
- athulappadan
- Austin Hicks
- bors
- Brian Anderson
- c4rlo
- Caleb Jones
- CensoredUsername
- cgswords
- changchun.fan
- Chiu-Hsiang Hsu
- Chris Stankus
- Christopher Serr
- Chris Wong
- clementmiao
- Cobrand
- Corey Farwell
- Cristi Cobzarenco
- crypto-universe
- dangcheng
- Daniele Baracchi
- DarkEld3r
- David Tolnay
- Dustin Bensing
- Eduard Burtescu
- Eduard-Mihai Burtescu
- Eitan Adler
- Erik Uggeldahl
- Esteban Küber
- Eugene Bulkin
- Eugene R Gonzalez
- Fabian Zaiser
- Federico Ravasio
- Felix S. Klock II
- Florian Gilcher
- Gavin Baker
- Georg Brandl
- ggomez
- Gianni Ciccarelli
- Guillaume Gomez
- Jacob
- jacobpadkins
- Jake Goldsborough
- Jake Goulding
- Jakob Demler
- James Duley
- James Miller
- Jared Roesch
- Jared Wyles
- Jeffrey Seyfried
- JessRudder
- Joe Neeman
- Johannes Löthberg
- John Firebaugh
- johnthagen
- Jonas Schievink
- Jonathan Turner
- Jorge Aparicio
- Joseph Dunne
- Josh Triplett
- Justin LeFebvre
- Keegan McAllister
- Keith Yeung
- Keunhong Lee
- king6cong
- Knight
- knight42
- Kylo Ginsberg
- Liigo
- Manish Goregaokar
- Mark-Simulacrum
- Matthew Piziak
- Matt Ickstadt
- mcarton
- Michael Layne
- Michael Woerister
- Mikhail Modin
- Mohit Agarwal
- Nazım Can Altınova
- Neil Williams
- Nicholas Nethercote
- Nick Cameron
- Nick Platt
- Niels Sascha Reedijk
- Nikita Baksalyar
- Niko Matsakis
- Oliver Middleton
- Oliver Schneider
- orbea
- Panashe M. Fundira
- Patrick Walton
- Paul Fanelli
- philipp
- Phil Ruffwind
- Piotr Jawniak
- pliniker
- QuietMisdreavus
- Rahul Sharma
- Richard Janis Goldschmidt
- Scott A Carr
- Scott Olson
- Sean McArthur
- Sebastian Ullrich
- Sébastien Marie
- Seo Sanghyeon
- Sergio Benitez
- Shyam Sundar B
- silenuss
- Simonas Kazlauskas
- Simon Sapin
- Srinivas Reddy Thatiparthy
- Stefan Schindler
- Stephan Hügel
- Steve Klabnik
- Steven Allen
- Steven Fackler
- Terry Sun
- Thomas Garcia
- Tim Neumann
- Tobias Bucher
- Tomasz Miąsko
- trixnz
- Tshepang Lekhonkhobe
- Ulrich Weigand
- Ulrik Sverdrup
- Vadim Chugunov
- Vadim Petrochenkov
- Vanja Cosic
- Vincent Esche
- Wesley Wiser
- William Lee
- Ximin Luo
- Yossi Konstantinovsky
- zjhmale
Комментарии (2)
14 ноября 2016 в 10:16
0↑
↓
use std::fs::File; fn main() { let f = File::create("foo.txt")?; }
rustc 1.13.0 (2c6933acc 2016-11-07) error[E0277]: the trait bound `(): std::ops::Carrier` is not satisfied -->
:4:13 | 4 | let f = File::create("foo.txt")?; | ^^^^^^^^^^^^^^^^^^^^^^^^ trait `(): std::ops::Carrier` not satisfied | = note: required by `std::ops::Carrier::from_error` error: aborting due to previous error Хм, я ожидал что благодаря введению специального оператора очень частая ошибка новичков — попытка пробросить ошибку из функции, которая не возвращает
Result
— получит внятное сообщение об ошибке:(14 ноября 2016 в 10:18 (комментарий был изменён)
+1↑
↓
Ну, Core Team сами пишут:
Однако в данном выпуске сообщения об ошибках использования
?
реализованы ещё не полностью.