Команда Rust рада представить выпуск Rust 1.19. Rust — это системный язык программирования, нацеленный на скорость, безопасность и параллельное выполнение кода.


Если у вас установлена предыдущая версия Rust, для обновления достаточно выполнить:


$ rustup update stable

Если же Rust еще не установлен, вы можете установить rustup с соответствующей страницы нашего веб-сайта и ознакомится с подробными примечаниями к выпуску Rust 1.19 на GitHub.


Что вошло в стабильную версию 1.19.0


В Rust 1.19.0 вошли некоторые долгожданные функции, но для начала, замечание для пользователей Windows. На этой ОС Rust использует для сборки link.exe, который входит в состав "Microsoft Visual C++ Build Tools". В последнем выпуске Visual Studio 2017 структура директорий для этих инструментов изменилась. Из-за этого вы были вынуждены использовать инструменты версии 2015 или изменять переменные среды (например, запуская vcvars.bat). В Rust 1.19.0, rustc знает, как найти инструменты версии 2017, поэтому вам не потребуется более использовать обходные пути.


А теперь к новым возможностям! Rust 1.19.0 — это первый выпуск, поддерживающий union (Объединения):


union MyUnion {
    f1: u32,
    f2: f32,
}

Объединения похожи на enum (Перечисления), но они не имеют "меток" в отличие от последних. "Метка" в enum позволяет узнать во время исполнения, какой вариант из перечисленных хранится в переменной.


Так как мы можем неверно интерпретировать данные, хранящиеся в объединении, и Rust не может проверить это за нас, чтение и запись полей объединений небезопасны:


let u = MyUnion { f1: 1 };

unsafe { u.f1 = 5 };

let value = unsafe { u.f1 };

Сравнение с образцом (Pattern matching) также работает:


fn f(u: MyUnion) {
    unsafe {
        match u {
            MyUnion { f1: 10 } => { println!("десять"); }
            MyUnion { f2 } => { println!("{}", f2); }
        }
    }
}

Когда объединения полезны? Главная сфера применения — взаимодействие с C. C API могут (и часто делают это) предоставлять объединения, так что написание оберток для API значительно упрощается. Также, из данного RFC:


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

Эту возможность ожидали с давних пор, и есть еще множество вещей, которые стоит улучшить. На данный момент, объединения могу включать только типы, реализующие Copy, и не могут реализовывать Drop. Мы надеемся убрать эти ограничения в будущем.


К слову, а вы когда-нибудь интересовались, как новые возможности добавляются в Rust? Объединения были предложены Josh Triplett, и он выступил на RustConf 2016, рассказав про процесс интеграции. Вы должны на это взглянуть!

К другим новостям, loop теперь могут возвращать значения через break:


// старый код
let x;

loop {
    x = 7;
    break;
}

// новый код
let x = loop { break 7; };

Rust традиционно позиционировал себя как "язык, ориентированный на выражения (expression)", большинство синтаксических конструкций возвращают значения, а не являются операторами (statement). loop несколько выделялся из этого ряда, не возвращая значение.


Что насчет остальных циклов? Еще не ясно, смотрите обсуждение в этом RFC.


Небольшое улучшение: замыкания, не захватывающие окружение, теперь могут быть приведены к указателю на функцию:


let f: fn(i32) -> i32 = |x| x + 1;

Теперь мы предоставляем архивы запакованные xz и используем их по умолчанию, что позволяет ускорить передачу данных. Сжатие с gzip все еще доступно, на случай если вы не можете использовать xz по какой-либо причине.


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


В заключение, о совместимости. Ранее, когда мы двигались к Rust 1.0, мы проделали большую работу, чтобы убедиться в том, что все отмечено как "стабильное" и "не стабильное". Однако мы просмотрели одну вещь: -Z флаги. -Z флаг компилятора включает нестабильные флаги. В отличие от всего остального, вы можете использовать -Z со стабильным Rust. В апреле 2016, в Rust 1.8, мы добавили предупреждение при использовании -Z со стабильной и бета версиями Rust. Более чем год спустя, мы починили эту дыру в нашей истории стабильности, запретив -Z везде, кроме ночных сборок компилятора.


Смотрите детальные заметки к выпуску для подробностей.


Стабилизация стандартной библиотеки


Крупнейшие изменения в стандартной библиотеке — макросы eprint! и eprintln!. Они работают точно так же, как print! и println!, но пишут в стандартный вывод ошибок, а не в стандартный вывод.


Другие изменения:



И некоторые стабилизированные API:



Смотрите детальные заметки к выпуску для подробностей.


Улучшения Cargo


В этом выпуске в Cargo были добавлены небольшие, но важные улучшения. Крупнейшее из них: Cargo теперь работает непосредственно с git-объектами. Это уменьшает размер реестра и ускоряет клонирование, особенно на Windows.


Другие улучшения:



Смотрите детальные заметки к выпуску для подробностей.


Разработчики 1.19.0


Множество людей участвовало в разработке Rust 1.19. Мы не смогли бы этого добиться без участия каждого из вас. Спасибо!


От переводчика
Благодарю ozkriff и gordon-f за помощь в переводе

Поделиться с друзьями
-->

Комментарии (37)


  1. ozkriff
    25.07.2017 13:16
    +7

    http://www.ncameron.org/blog/rustfmt-changes


    Хочу отметить что для своего проекта уже почти месяц как пользуюсь rustfmt-nightly и в целом оно вполне хорошо уже и сейчас работает. А nrc еще и активно его пилит — с каждой новой версией единый стиль форматирования ржавого кода становится все ближе и ближе.




    Кстати, с удивлением понимаю что даже в рамках ржавого сообщества совершенно не известна такая отличная штука как cargo-apk, которая сама из обычного графического glutin-приложения собирает apk под андроид. Для ее работы надо или просто поставить sdk/ndk, или использовать стандартный докер-образ. Очень рекомендую ознакомиться :)


    1. DarkEld3r
      25.07.2017 13:55
      +1

      Мы тоже форматом пользуемся, хотя по поводу форматирования массивов были жаркиe споры.


      1. ozkriff
        26.07.2017 13:31
        +1

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


        Ну и #[cfg_attr(rustfmt, rustfmt_skip)] никто не отменял, особенно если требуется еще большие массивы вставить :)


        1. DarkEld3r
          26.07.2017 13:45

          Да меня тоже дефолтный вариант вполне устраивает, а вот rustfmt_skip как раз не особо нравится: его на целую функцию вешать придётся. Можно, конечно, вынести создание массива в отдельную функцию, но это уже много лишних действий. Да и есть опция array_horizontal_layout_threshold, но пока только в "ночном" формате.


    1. anatoly62
      25.07.2017 17:08
      +1

      Почему не известна. Использую уже около года для сервера под андроид.


      1. ozkriff
        26.07.2017 13:27

        Ну я не буквально про всех, конечно) Просто по разговорам понял что многие, кому я говорил о нем в чатиках тут и там, про cargo-apk слышали впервые.


  1. denis_obrezkov
    25.07.2017 13:29
    +2

    Интересно, если Rust, как системный язык программирования, нацелен на безопасность, если ли для него что-то подобное инструментам Frama-C для языка C и SPARK для языка Ada? И как обстоят дела со статическими анализаторами для Rust?


    1. ozkriff
      25.07.2017 13:31
      +2

      как обстоят дела со статическими анализаторами для Rust?

      мне первым дело в голову приходит https://github.com/Manishearth/rust-clippy/


    1. Revertis
      25.07.2017 13:37
      +3

      Так там же сам компилятор очень серьёзно статически анализирует код :)
      В Расте довольно сложно написать херню.


      1. BratSinot
        26.07.2017 02:34

        Очень легко, как минимум в unsafe. А вообще, как вы выразились, «херня» понятие растяжимое.


        1. ozkriff
          26.07.2017 13:44
          +1

          Очень легко, как минимум в unsafe.

          На то он и unsafe.


          Но по этому повод есть мысль: в большом проекте на все файлы должно распространяться #![deny(unsafe_code)], а на пару файлов с необходимыми unsafe-абстракциями CI-робот должен требовать r+ от пары старших разработчиков проекта.


          А вообще, как вы выразились, «херня» понятие растяжимое.

          А так — да, проще всего вспомнить про ту же возможность утечки памяти в безопасном коде.


    1. ozkriff
      25.07.2017 13:38
      +1

      если ли для него что-то подобное инструментам Frama-C для языка C и SPARK для языка Ada?

      про формальную верификацию вот тут — https://brson.github.io/2017/07/10/how-rust-is-tested#s-form — есть актуальные сылки. В частности на rustbelt.


      Но, насколько я знаю, это пока все очень сырое.


  1. ozkriff
    25.07.2017 13:47
    +1

    А, еще из обновок за этот месяц: выкатили альфа версию официального расширения для vscode — rls-vscode. Работает через RLS, само собой, пилится его же авторами. Пользуюсь пока полторы недели: не без шероховатостей (в них обычно виноваты недоработки RLS), но в целом опыт более положительный чем с vscode-rust и выглядит как-то перспективней.


    1. Halt
      25.07.2017 17:57
      +1

      Я последнее время использую плагин от Калиты. В чем отличия и имеет ли смысл перелезать на официальный вариант?


      1. ozkriff
        26.07.2017 13:48

        Прямо сейчас может и не имеет особо смысла прыгать. Из мгновенных последствий перехода — с ним RLS чуть реже падает. Но вот в будущем мне верится что rls-vscode станет доминирущим просто из-за официальной поддержки — его пилят те же чуваки что и RLS, так что это расширение с самого начала полностью полагается на RLS, очень тесно с ним интегрировано и фактически является просто тонким клиентом к нему, почти без своей логики на typescript.


  1. ozkriff
    25.07.2017 14:02
    +2

    В оригинале статьи, если я все правильно понимаю, неточность:


    union MyUnion {
        f1: u32,
        f2: f32,
    }
    
    fn main() {
        let mut u = MyUnion { f1: 1 };
        u.f1 = 5;
        let value = unsafe { u.f1 };
        println!("value = {}", value);
    }

    https://play.rust-lang.org/?gist=fd55cfbbcfca58df996eae30fa6446f6


    ^ запись Copy-полей работает без unsafe, а только такие поля сейчас и разрешены (реддит).


    1. Arekusei
      25.07.2017 17:08

      Как-то странно это выглядит. Можно в итоге при записи вытворять поведение которое создаст баги и компилятор в итоге ничего не скажет https://play.rust-lang.org/?gist=f991ba49c62230c6b876a4755ba5ae08&version=stable


      1. vitvakatu
        25.07.2017 17:10
        +2

        Да, конечно, в этом и есть суть unsafe. Если небезопасный код присутствует у вас в программе — вы должны четко понимать, зачем он вам.


        1. Arekusei
          25.07.2017 17:17

          Ну мне кажется странным то что запись является safe. По факту и то и другое в совокупности является unsafe.
          Хотя само чтение уже всё же привлекает внимание что нужно быть осторожным.


          1. vitvakatu
            25.07.2017 17:25
            +2

            Но запись никак не может повредить программе. А вот чтение — очень даже может.


          1. ozkriff
            26.07.2017 13:54
            +2

            В языке уже в любом случае с самого начала есть сырые указатели — у их очень похожая ситуация: взятие и передача любого сырого указателя безопасна, а разыменование — опасно. Так что такое поведение вполне в рамках философии языка.


  1. denis_obrezkov
    25.07.2017 16:36

    Интересно, а как Rust привлекает к себе новых пользователей? Adacore, к примеру, уже второй год подряд проводит конкурс MakeWithAda. Какая мотивация у разработчиков начать свой новый проект на Rust?


    1. chabapok
      25.07.2017 17:44
      +2

      Зачем привлекать? Мотивация очень простая: а не то придется писать на плюсах и потом ловить сегментации на проде.


      1. denis_obrezkov
        25.07.2017 18:07

        Так ведь можно (на самом деле нет) писать на языке Ada. Вот чем Rust хорош для системного программирования? Все BSP написаны на C, некоторые платформы, особенно с закрытым набором команд, поддерживают только С.

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


        1. chabapok
          25.07.2017 20:25
          +3

          Для системного программирования раст хорош тем, что лучше продуман в плане безопасности. Если си подходит к решению задачи «можно все, но если напортачишь — ты ССЗБ» — то раст подходит к решению этой задачи с обратной стороны. Запрещено все, исключая белый список. Есть проверки выхода за границы массивов и тд. И поэтому ты не напортачишь так чтоб совсем все плохо стало. Вызвать сегментацию или UB можно только используя блок unsafe. И эти блоки — маленькие.
          Кроме того, есть мелкие ништяки: например стрикт алиасинг в си идет для единиц трансляции, а в раст — каждом месте использования. Указатели на виртуальную таблицу в раст хранятся вместе с указателем, а не с данными (свои плюсы и минусы).

          То, что BSP написаны на С — тоже не технический момент, а «организационный», и исторически сложившаяся в текущий момент времени данность. Раст, кстати, сопрягается с другими языками. В первую очередь с си. Так что вобщем «в железо» это тоже можно шить, теоретически. Но там скорей всего придется шпарить без растовской stdlib.

          Думаю, си и плюсы — устарели, и их преимущество лишь в том, что к ним много написано кода и много есть специалистов.


          1. denis_obrezkov
            25.07.2017 21:01

            Вот и возникает вопрос, если C и С++ устарели, и их преимущество лишь в количестве кода и количестве специалистов, то как этих специалистов переманить на сторону Rust и увеличить при этом количество кода для Rust?


            1. chabapok
              26.07.2017 10:14
              +3

              Специалисты бывают двух типов:
              1 — я знаю технологию Х — буду делать на ней и ничего нового ненужно
              2 — появилась технология У, которая лучше Х, давайте все перепишем на У

              Первый тип никак не переманишь — второй сам перейдет и будет влиять на приток новых специалистов.


              1. denis_obrezkov
                26.07.2017 11:09
                +1

                Ну, делить людей на два лагеря, это, наверное, немного некорректно. Взять, к примеру, меня и язык Ada. Я давно хотел его попробовать, но как-то все руки не доходили, а тут появился конкурс makewithada. И я подумал, что можно ведь и поучаствовать. Другое дело, что я так и не смог создать на Ada нормальное приложение — язык показался мне неоправданно сложным и многословным. Но я его при этом попробовал и сформировал о нем свое мнение.


              1. amarao
                26.07.2017 12:33
                +2

                Есть ещё третий тип: Я никогда этого не делал, на чём мне его сделать? Выбираю…


                1. grossws
                  26.07.2017 14:41

                  Или пробовал то, то и то. В данном случае мне больше подходит/импонирует такой-то язык/платформа/экосистема.


        1. anatoly62
          26.07.2017 13:12
          +4

          Ничего не мешает на нем писать прикладное ПО. У меня уже несколько клиент-серверных приложений, где и сервер и клиент написаны на Rust.


    1. Halt
      25.07.2017 18:00
      +1

      Например вот таквот так).

      А видя все это, люди сами подтягиваются.


  1. amarao
    26.07.2017 12:31

    loop, возвращающий значение, это что-то взрывающее мозг.

    let the_answer_to_everything: i8 = loop{ if false {break 42;}};

    То есть мы записываем значение 42 в the_answer_to_everything, но только никогда.


    1. ozkriff
      26.07.2017 14:04
      +3

      я надеюсь что clippy скоро подтянется и будет ругаться на такое. как раз для него задача


      Так-то этот код особо не отличается от старого


      let the_answer_to_everything: i8;
      loop {
          if false {
              the_answer_to_everything = 42;
              break;
          }
      }


      1. amarao
        27.07.2017 10:10

        Я это понимаю, но экзестенциальная проблема остаётся: loop возвращает значение, не смотря на то, что он бесконечный цикл.


        1. ozkriff
          27.07.2017 10:28

          Экзестенциальная проблема есть конечно, но она же и раньше была уже. Я не вижу принципиальных отличий с точки зрения "экзестенциальной проблемности" от


          fn f(should_stop: bool, n: i64) -> i64 {
              if !should_stop {
                  f(should_stop, n + 1)
              } else {
                  0
              }
          }
          
          fn main() {
              let n = f(false, 0);
              println!("n = {}", n);
          }

          Тоже вот ожидается что функция вернет значение, а она стек лопнет и грохнет все. Был бы стек бесконечный или сработала бы хвостовая оптимизация (edit: в release сборке срабатывает, кстати) — был бы полный аналог кода с loop.


          ^ Без should_stop булки, кстати, будет предупреждение warning: function cannot return without recurring — приятная мелочь.


          1. amarao
            27.07.2017 18:55

            Вы описываете другой случай: по синтаксису функция должна возвращать значение, а она делает сайд-эффект. В принципе, это вопиюще, но все привыкли.

            А вот к обратному не привыкли: с точки зрения системы типов у меня написано, что бесконечный цикл возвращает значение. Это что-то из матанализа — после того, как мы n устремили к бесконечности, мы получаем результат и добавляем его к значению другой величины.