LINUX.ORG.RU

Rust 1.13

 


4

10

Представлен релиз 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.

Стабилизация языка

Стабилизация библиотек

Возможности Cargo

Более детальный список изменений доступен по ссылке: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1130-2016-1...

>>> Подробности

★★★★★

Проверено: maxcom ()
Последнее исправление: cetjs2 (всего исправлений: 5)

Ответ на: комментарий от Safort

Жду бомбящих по поводу оператора ?

Не бомблю, но думал, что если его и введут, то для Option. А так оно и нелогично выглядит (? и вдруг аж выход из функции), и дублирует макрос, который по хорошему теперь надо бы выкинуть.

anonymous
()
Ответ на: комментарий от Safort

Жду бомбящих по поводу оператора ?

на редите уже пробуют новую утилиту untry которая заменяет в коде все try! на ?

pftBest ★★★★
()

оно уже пригодно, чтобы написать на нём TODO-list? А потом рассказать на модной конференции разработчиков?

Alve ★★★★★
()

Походу mozilla знает толк в пиаре. Молодцы.

Тут ещё предыдущий расто-срач не закончился, а уже новый намечается.

С момента релиза 1.0 раст из топа лора хоть раз вылезал?

Ivan_qrt ★★★★★
()

The new constructs are:

An ? operator for explicitly propagating «exceptions».

A catch { ... } expression for conveniently catching and handling «exceptions».

...

We can accomplish this by adding constructs which mimic the exception-handling constructs of other languages in both appearance and behavior, while improving upon them in typically Rustic fashion.

У них там табу что ли на слово exception? Если это exception, то и называли бы его exception, ан нет. Сектанты какие-то. Решили когда-то, что исключения - зло и теперь не могут признаться, что были неправы.

asaw ★★★★★
()
Ответ на: комментарий от buddhist

Что такое, в Go уже появились параметризуемые типы? Если нет, то твой коммент в лучшем случае провокация флейма.

tailgunner ★★★★★
()
Ответ на: комментарий от asaw

Если это exception, то и называли бы его exception

Это возврат значения. Exception это могут называть только люди с синдромом утенка.

tailgunner ★★★★★
()
Ответ на: комментарий от asaw

Тебе же написали: «mimic the exception-handling constructs of other languages in both appearance and behavior».

Это написали тебе. Для объяснения концепции возврата значения людям с синдромом утенка вполне подойдет и слово «exception».

tailgunner ★★★★★
()
Ответ на: комментарий от asaw

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

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

Ну так в расте же нельзя просто так взять и случайно проигнорировать ошибку? Или можно? А то, очем ты написал - просто детали реализации.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Ну так в расте же нельзя просто так взять и случайно проигнорировать ошибку? Или можно?

Можно полностью проигнорировать возвращаемое значение, например.

Manhunt ★★★★★
()

Оператор ? заменяет весь код обработки ошибок, написанный при помощи оператора match ранее. Иными словами, ? применяется к значению Result, и если оно равно Ok, разворачивает его и отдаёт вложенное значение; если это Err, то происходит возврат из функции, в которой вы находитесь.

Ещё немного, и ребята изобретут монады.

hateyoufeel ★★★★★
()
Ответ на: комментарий от tailgunner

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

buddhist ★★★★★
()
Ответ на: комментарий от ykroop

Для этого как бы и пилится, чтобы выкинуть C++ из проектов Mozilla.

Когда они выкинут C++ из Firefox, я соответственно выкину Firefox.

anonymous
()
Ответ на: комментарий от anonymous

я соответственно выкину Firefox

Нет-нет, не делай этого, пожалуйста! Умоляем тебя все сектой!! Что угодно, но только не это!

Manhunt ★★★★★
()
Ответ на: комментарий от asaw

Ого, это уже какой-то раскол внутри секты.

Пока больше похоже на раскол внутри твоей черепной коробки...

Manhunt ★★★★★
()
Ответ на: комментарий от tailgunner

Так это и есть секта, а ты - сектант, раз ты воспринимаешь как личный выпад критику, которая не обращена к тебе.

asaw ★★★★★
()
Ответ на: комментарий от buddhist

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

Не знаю насчет «того треда», но кортеж вместо Result плох тем, что можно использовать поле кортежа, которое то определено, то не определено (в зависимости от того, произошла ошибка или нет). В Rust такое невозможно.

tailgunner ★★★★★
()
Ответ на: комментарий от asaw

Так это и есть секта, а ты - сектант, раз ты воспринимаешь как личный выпад критику, которая не обращена к тебе.

См. выше.

tailgunner ★★★★★
()
Ответ на: комментарий от hateyoufeel

Надо еще больше базвордов - по ним можно отсеивать тех, кто не в теме!..

yyk ★★★★★
()
Ответ на: комментарий от tailgunner

они знают о монадах и не в восторге от них.

А от простыней match они были в восторге? И как переопределить оператор ? если мне нужен чуть более сложный тип для обработки ошибок чем Result?

Я в принципе не агитирую конкретно за копирование вещей из хацкелла, но ИМХО добавлять решение для частного случае там, где лучше охватить общий, это немного странно. Алсо было бы очень круто иметь возможность самому определять бинарные операторы, но это видимо совсем фантастика.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 3)
Ответ на: комментарий от hateyoufeel

А от простыней match они были в восторге?

От редких вложенных гроздей try!, ты хочешь сказать. Они к ним относились спокойно.

если мне нужен чуть более сложный тип для обработки ошибок чем Result?

А в чем проблема? Делаешь свой сколь угодно сложный тип и возвращаешь его в качестве ошибочного варианта Result.

ИМХО добавлять решение для частного случае там, где лучше охватить общий, это немного странно

Я не фанат оператора ?, но некоторым нравится. Посмотрим.

tailgunner ★★★★★
()
Ответ на: комментарий от buddhist

Выше говорят, что в расте тоже так можно.

Выше чушь говорят. Проигнорировать можно, но для этого надо явно писать unwrap()

Zenom ★★★
()
Ответ на: комментарий от hateyoufeel

Ещё немного, и ребята изобретут монады.

Result и Option и так являются вполне полноценными монадами. Просто в языке нет сахара типа do-нотации.

Zenom ★★★
()
Ответ на: комментарий от Zenom

Проигнорировать можно, но для этого надо явно писать unwrap()

unwrap не игнорит, он паникует.

O02eg ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.