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)

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

enum IResult<a,b>=More(Fun<a,IResult<a,b>)|Done(b)|Failure(str)

И по ? Хотелось бы пробрасывать наверх Failure и More.

А Either из трех вариантов не хотелось бы? Обработка ошибок в Rust построена на том, что функция отрабатывает либо нормально, либо с ошибкой. Третьего варианта нет.

Чем тред про HKT на реддит закончился

Если ты о треда, в которых обсуждается цикл статей Нико, то ничем (естественно). Если о решении реализовывать HKT, то оно пока не принято.

раст когда-нибудь станет юзабельным

У Rust нет цели быть юзабельным для всех.

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

Да он всё правильно сказал. Если вам удобен питон - то руст вам не нужен.

Rust - это C++, из которого повыпиливали кучу устаревшего говна и прикрутили новые модные стильные молодёжные плюшки, сделав упор на безопасность работы с памятью

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

Rust - это C++, из которого повыпиливали кучу устаревшего говна и прикрутили новые модные стильные молодёжные плюшки

Rust - это Си, в который прикрутили старые плюшки из ML и не очень старые плюшки из Cyclone.

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

Сейчас придут хардкорные сишники и расскажут, что можно и на Си писать безопасно, а сахар раста реализуется через макросы - итого раст не нужен.

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

Мне сходу неочевидно, что ассоциированными типами можно полноценно заменить HKT, нужно читать сильно внимательнее и думать.

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

Там целая серия постов: http://smallcultfollowing.com/babysteps/blog/2016/11/09/associated-type-const...
Во втором показывается, как можно реализовать HKT через ATC.

quantum-troll ★★★★★
()
Последнее исправление: quantum-troll (всего исправлений: 1)
Ответ на: комментарий от IPR

Объясните толком, зачем оно нужно? Вот изучаю Питхон, может что-то не то делаю? Чем оно лучше?

Быстрее работает, потребляет меньше памяти, не имеет непредсказуемых задержек из-за сборки мусора. Обычно для тех задач, где используют пайтон, это не имеет большого значения, поэтому сравнение не имеет большого смысла. Сравнивать надо с C, C++.

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

Сейчас придут хардкорные сишники и расскажут, что ... сахар раста реализуется через макросы - итого раст не нужен

Пусть приходят скорее! А то второй день пишу контейнер на си, а получается так и сяк говно.

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

Или вы думаете, что тут проплаченные троли сидят?

Нет, что вы. Именно в этом и проявляется талант мозиллы, они очень хорошо заставили толпу школьников работать на себя. Даже свифт от яббла, собаку съевшего на пиаре, забыт на фоне раста.

Где вы пиар увидели?

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

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

А для Either с тремя конструкторами есть разумная общая семантика для Monad? Вот, например, тут она есть, если done продолжаем, если Fail пробрасываем, если NeedMore, то меняем continuation и возвращаем наверх. Т.е. Result<Done,Result<Next,Failure>> не будет аналогом. При этом ? Синтаксис вполне бы мог работать.

У Rust нет цели быть юзабельным для всех.

Досрочно быть юзабельным для меня, же :)

qnikst ★★★★★
()
Ответ на: комментарий от quantum-troll

Я его по диагонали смотрел, мне было неочевидно что все интересные случаи покроются.

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

Даже свифт от яббла, собаку съевшего на пиаре, забыт на фоне раста.

Потому, что это лор. Тут swift никому не впал. Тем временем swift в разы популярнее rust.

Раст самая обсуждаемая тема на лоре.

О да! Мозилла во всю работает на публику лора. Не смешите. Вы берёте реальную статистику, но делаете из неё неверные выводы.

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

Result<Done,Result<Next,Failure>>

Это быдлокод в мире раста. Нужно объединить в один enum и реализовать нужный трейт From.

Ну или вообще разделить метод на два.

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

А для Either с тремя конструкторами есть разумная общая семантика для Monad?

Думаю, нет. Так же, как для Result с тремя конструкторами.

тут она есть, если done продолжаем, если Fail пробрасываем, если NeedMore, то меняем continuation и возвращаем наверх

И тут ее нет. Fail - это ошибка, Done и NeedMore - нормальное завершение. Поэтому Result<Fail, enum { Done, NeedMore(whatever) }>.

У Rust нет цели быть юзабельным для всех.

Досрочно быть юзабельным для меня, же :)

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

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

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

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

В том, что панику не предполагается использовать для обработки ошибок, только для сигнализации «нарушений контрактов». Исключения же в C++/Java/C# используются и для того, и для другого.

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

Паника же задумывается как сигнал о том, что произошло то, что не должно было происходить. Память совсем полностью закончилась, требования к аргументам нарушены, или ещё какой-нибудь ассерт нарушен. Этих ситуаций следует избегать.

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

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

Не нужно это обрабатывать для веб-сервера. Если не нашел файл, то кидаешь выше, где обрабатываются все ошибки и не страдаешь херней.

foror ★★★★★
()

Представлен релиз Rust 1.13 — системного языка программирования, нацеленного на безопасную работу с памятью, скорость и параллельное выполнение кода.

На данный момент это меньшее из зол среди легаси и хипарей. Так наверное и не дождёшься, когда нормальный ЯП запилят, хоть сам садись и пили...

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

Это значит, паника аналогична std::logic_error

и сегфолту

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

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

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

Паника же задумывается как сигнал о том, что произошло то, что не должно было происходить. Память совсем полностью закончилась, требования к аргументам нарушены, или ещё какой-нибудь ассерт нарушен. Этих ситуаций следует избегать.

Тем не менее, в реальности они случаются, и ронять программу из-за этого как-то слишком резко. Так что в Rust паника скорее ближе к механизму set_unexpecteed.

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

И в чём разница по-твоему?

В назначении. Исключения - это метод информирования программы об ошибках. Исключения должны ловиться (в идеале). Паника это метод убиения программы/потока, с корректным освобождением ресурсов и информирования о беде. Ошибки возвращаются через Option/Result.

Exception нормально ловить на уровень выше генерации, на два. Panic если и ловят, то на самом верхнем уровне, чтобы залогировать или красиво вывести сообщение перед смертью.

Если вы планируете массово ловить паники и продолжать программу, что у вас что-то очень не так.

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

Потом сделали перехватываемую панику.

Не для этого. Просто критические ошибки надо логировать. А так можно и грабли вместо палки-чесалки использовать. Было бы желание.

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

if читается не хуже (если даже не лучше) тернарного оператора. А вот try!, наверное, решили заменить именно по этой причине, в частности из-за скобкоты.

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

щито?

test.c:2:9: error: expected expression
        return if (x != 0) {42} else {7};
               ^

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

Просто критические ошибки надо логировать.

Ошибки надо обрабатывать («allowing a graceful handling of the error»). То, что обработка должна сводится к логгингу - не более, чем твое личное мнение.

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

только для самых страшных ошибок

Да, но для большинства ошибок стандартная практика – Result<T, E> в случае Rust, throws в случае Swift (синтаксический сахар поверх возвратных значений) и кортежи в случае Go. В Swift, кстати, некоторые API Objective-C бросают исключения, для которых нужны костыли вроде этого.

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

Читай весь диалог внимательнее чтобы понять почему я говорю что это плохо и почему "?" Частичное решение.

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

Пусть приходят скорее! А то второй день пишу контейнер на си, а получается так и сяк говно.

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

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

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

Так в Си тоже.

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

А в Си даже Hello world нельзя написать не переходя к небезопасному коду, лол.

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

не более, чем твое личное мнение.

В доке рекомендации пока никто не менял. Ошибка, на которую можно положить - Option. Нельзя - Result. Нельзя продолжать - panic.

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

В доке рекомендации пока никто не менял

Процитируй рекомендацию.

Нельзя продолжать - panic.

Не вижу противоречия с «graceful handling», вариантом которой может быть продолжение работы программы в целом.

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

Тогда зачем ты это здесь написал? Я говорил совсем про другое.

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

Так в Си тоже.

И в чем тогда смысл такой безопасности, если постоянно приходится бороться с borrow checker, а половина кода так и остается небезопасной?

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

И в чем тогда смысл такой безопасности, если постоянно приходится бороться с borrow checker

Говорят, есть люди, которые не борются с borrow checker, а используют его.

а половина кода так и остается небезопасной?

Если ты хотел спросить «в чем преимущество Rust перед Си, кроме memory safety», то ответ - «ADT, RAII, дженерики, модули, стандартная система сборки».

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

а половина кода так и остается небезопасной

Если половина то ССЗБ. А если небезопасных строчек пара десятков, тогда есть смысл.

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

Кому-то не мешало бы к психоаналитику.

Ну так это в православный храм, к батюшке, лучше всего к тому, который вам знаком. :)

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

И в чем тогда смысл такой безопасности, если постоянно приходится бороться с borrow checker, а половина кода так и остается небезопасной?

В том, что другая половина остаётся безопасной.

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

А что ответить человеку, у которого половина кода - связный список? Пожелать получить зачет!

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