Представлен релиз Rust 1.13 — системного языка программирования, нацеленного на безопасную работу с памятью, скорость и параллельное выполнение кода. В этот релиз вошли 1448 патчей.
Этот сезон оказался очень плодотворным для Rust. Проведены конференции RustConf, RustFest и Rust Belt Rust. Обсуждено будущее языка, разработан план на 2017 год и созданы новые инструменты.
Новое в 1.13
Выпуск 1.13 содержит несколько расширений языка, включая долгожданный оператор ?
, оптимизацию времени компиляции, новые возможности cargo и стандартной библиотеки, а также множество мелких улучшений в документации и исправления ошибок.
Cargo в этом релизе содержит важные обновления безопасности, связанные с зависимостями от curl и OpenSSL, для которых также недавно были опубликованы обновления безопасности. Подробную информацию можно найти в соответствующих источниках для curl 7.51.0 и OpenSSL 1.0.2j.
Оператор ?
Добавлен новый оператор ?
, делающий обработку ошибок приятнее за счёт уменьшения визуального шума. В качестве иллюстрации представим следующий код, который считывает некоторые данные из файла:
fn read_username_from_file() -> Result<String, io::Error> {
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
. Для этого необходимо обрабатывать результаты ввода-вывода. В простых ситуациях, как здесь, где требуется лишь возвратить ошибку выше по стеку вызовов, постоянное написание кода обработки по одному и тому же шаблону — это излишняя информация и вряд ли будет содержать полезную информацию для читателя.
С оператором ?
, вышестоящий код выглядит следующим образом:
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("username.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
Оператор ?
заменяет весь код обработки ошибок, написанный при помощи оператора match
ранее. Иными словами, ?
применяется к значению Result
, и если оно равно Ok
, разворачивает его и отдаёт вложенное значение; если это Err
, то происходит возврат из функции, в которой вы находитесь.
Более опытные пользователи могут заметить, что этот оператор делает то же самое, что и макрос try!
, который доступен начиная с Rust 1.0. И будут правы, в самом деле, это то же самое. До 1.13 read_username_from_file
можно было бы написать следующим образом:
fn read_username_from_file() -> Result<String, io::Error> {
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!
, код сложнее для восприятия и может отвлекать внимание от основного кода, как в данном случае.
И наконец, отдельный синтаксис позволит в будущем сделать улучшенные сообщения об ошибках с учетом оператора ?
, тогда как это в целом сложно сделать для кода, основанного на макросах.
Более подробно об операторе ?
можно прочитать в RFC 243.
Улучшение производительности
В последнее время очень много внимания заострено на производительности компилятора. Mark Simulacrum и Nick Cameron произвели улучшения http://perf.rust-lang.org/, инструмента для отслеживания производительности компилятора, на котором периодически запускается набор rustc-benchmarks на выделенном оборудовании. Инструмент записывает результаты каждого прохода компилятора и позволяет находить и отслеживать код, приведший к регрессии. Например, при помощи этого инструмента можно посмотреть график производительности за весь цикл разработки релиза 1.13, где можно увидеть заметное сокращение времени работы компилятора, отдельно представленное на соответствующей странице со статистикой.
Большое улучшение на графике от 1 сентября связано с оптимизацией от Niko по кешированию нормализованных проекций во время преобразования. То есть во время генерации промежуточного представления LLVM компилятор больше не пересчитывает каждый раз конкретные экземпляры связанных типов, когда они необходимы, а использует ранее вычисленные значения. Несмотря на то, что данная оптимизация не влияет на всю кодовую базу, для некоторого кода с определенным шаблоном, например, futures-rs, ускорение сборки в режиме отладки достигает 40%.
Другая оптимизация от Michael Woerister уменьшает время компиляции библиотек, экспортирующих множество встраиваемых функций. Когда функция помечена как «#[inline]», в дополнение к преобразованию этой функции в текущей библиотеке компилятор сохраняет её представление MIR и преобразует функцию в представление LLVM в каждой библиотеке, которая вызывает её. Оптимизация, сделанная Michael Woerister, позволяет компилятору избегать предварительных преобразований кода встраиваемых функций в библиотеках, в которых они определены, до их непосредственного прямого вызова. Таким образом, компилятор избавляется от необходимости выполнения лишних шагов по преобразованию функции в промежуточное представление LLVM, оптимизации LLVM и преобразования функции в машинный код.
В некоторых случаях это приводит к впечатляющим результатам. Например, время сборки библиотеки ndarray уменьшилось на 50%, а библиотека winapi 0.3 (ещё не опубликована) полностью избавилась от шага генерации машинного кода.
Но это ещё не всё: Nick Nethercote обратил своё внимание на производительность компилятора, сконцентрировавшись на профилировании и микрооптимизациях. Этот релиз включает в себя некоторые плоды его работ, ещё больше ожидается в 1.14.
Другие заметные изменения
Макросы теперь можно использовать на позиции типов (RFC 873), а атрибуты могут быть применены к операторам (RFC 16):
// Use a macro to name a type
macro_rules! Tuple {
{ $A:ty,$B:ty } => { ($A, $B) }
}
let x: Tuple!(i32, i32) = (1, 2);
// Apply a lint attribute to a single statement
#[allow(uppercase_variable)]
let BAD_STYLE = List::new();
Были удалены встраиваемые флаги сброса. Раньше при условном перемещении компилятор встраивал «флаг сброса» в структуру (увеличивая его размер), чтобы отслеживать, когда надо его сбросить. Из-за этого некоторые структуры занимали больше места, что мешало передаче типов с деструкторами поверх FFI. Благодаря тому, что в версии 1.12 добавлен MIR, появилась основа для многих улучшений, включая удаление встраиваемых флагов сброса. Теперь флаги сброса хранятся в дополнительном слоте в стеке тех функций, которым они нужны.
Релиз 1.13 содержит серьёзную ошибку в генерации кода для ARM с аппаратной реализацией чисел с плавающей точкой. Поскольку 1.13 содержит исправление безопасности, пользователям ARM рекомендуется использовать бета-версии 1.14, в которых скоро появится исправление для ARM.
Стабилизация языка
- Типаж Reflect объявлен устаревшим. Смотрите пояснение, что это значит для 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
Более детальный список изменений доступен по ссылке: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1130-2016-1...
>>> Подробности