Выпуск Rust 1.18

https://blog.rust-lang.org/2017/06/08/Rust-1.18.html
  • Перевод

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


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


$ rustup update stable

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


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


Как и всегда, Rust 1.18.0 собрал в себе множество улучшений и новых возможностей.


Одно из крупнейших и самых ожидаемых изменений: члены команды Carol Nichols и Steve Klabnik пишут новую редакцию "Язык программирования Rust", официальной книги о Rust. Она пишется открыто на GitHub, и уже более ста человек внесли в нее свой вклад. Этот выпуск включает первый черновой вариант второго издания в нашей онлайн документации. 19 из 20 глав уже написаны, черновой вариант 20 главы будет добавлен в выпуске Rust 1.19. Когда книга будет завершена, версия для печати будет доступна через No Starch Press, если вы предпочитаете бумажную копию. Мы все еще работаем совместно с редакторами No Startch, чтобы улучшить текст, но мы бы хотели представить книгу широкой аудитории уже сейчас.


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


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


pub(crate) bar;

Выражение внутри () является 'ограничением', уточняющим область видимости. Использование ключевого слова crate в примере выше означает, что bar будет публичным для всего контейнера (crate), но не вне него. Это упрощает создание API, которые "публичны для вашего контейнера", но не доступны вашим пользователям. Это было возможно с существующей системой модулей, но очень часто выглядело неудобно.


Вы также можете указать путь, например:


pub(in a::b::c) foo;

Это означает "foo публично внутри иерархии a::b::c, но нигде больше". Эта особенность была определена в RFC 1422 и описана в документации.


Для пользователей Windows, Rust 1.18.0 имеет новый атрибут, #![windows_subsystem]. Это работает так:


#![windows_subsystem = "console"]
#![windows_subsystem = "windows"]

Эти выражения контролируют флаг /SUBSYSTEM компоновщика. В настоящий момент, доступны только console и windows.


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


Наконец, кортежи, перечисления и структуры (без #[repr]) всегда имели неопределенное размещение в памяти. Мы включили автоматическое переупорядочивание, что может привести к меньшим размерам структур.


Представьте следующую структуру:


struct Suboptimal(u8, u16, u8);

В предыдущих версиях Rust на платформе x86_64, эта структура будет занимать в памяти шесть байт. Но смотря на код, вы ожидаете только 4. Дополнительные два байта появляются из-за выравнивания: так как наибольший тип в структуре u16, она должна быть выравнена по два байта. Но в этом случае u16 расположено в памяти со смещением в один байт. Чтобы разместить его со смещением в два байта, нужно добавить еще один байт выравнивания после первого u8. Добавив еще один байт после второго u8, получаем:


1 + 1 (выравнивание) + 2 + 1 + 1 (выравнивание) = 6 байт.


Но что, если наша структура выглядит так?


struct Optimal(u8, u8, u16);

Эта структура выровнена оптимально; u16 лежит с выравниванием в два байта, как и вся структура в целом. Никакого выравнивания не требуется. Это дает нам 1 + 1 + 2 = 4 байт.


Проектируя Rust, мы оставили детали размещения в памяти неопределенными именно по этой причине. Не придерживаясь определенной политики, мы можем вносить оптимизации, например, как в этом случае, когда компилятор может оптимизировать Suboptimal в Optimal автоматически. И если вы проверите размеры Suboptimal и Optimal в Rust 1.18.0, вы увидите, что они обе имеют размер 4 байта.


Мы планировали это изменение в течение длительного времени; предыдущие версии Rust включали эту оптимизацию в ночных (nightly) сборках, но некоторые люди писали небезопасный код, который требовал точных данных о размещении в памяти. Мы откатили это изменение и исправили все подобные случаи, о которых знали. Но если вы найдете какой-нибудь код, который работает неправильно, сообщите нам, чтобы мы смогли его исправить!


Мы планировали перенести rustdoc на Markdown-совместимый парсер CommonMark в течение долгого времени. Однако, простой переход может привести к проблемам, так как спецификация CommonMark отличается от нашего текущего парсера, Hoedown. Как часть нашего плана перехода, новый флаг был добавлен в rustdoc, --enable-commonmark. Этот флаг включает использование нового парсера вместо старого. Пожалуйста попробуйте его! Насколько мы знаем, оба парсера производят одинаковые результаты, но мы хотим знать, если вы найдете сценарий, при котором их результаты отличаются!


Наконец, компиляция самого rustc теперь на 15%-20% быстрее. Сообщения коммитов в этом PR содержат некоторые детали; существовало множество неэффективных мест, но теперь все они исправлены.


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


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


Семь новых API были стабилизированы в этом выпуске:


  • Child::try_wait, неблокирующая версия Child::wait.
  • HashMap::retain и HashSet::retain добавляют retain из API Vec<T> для этих двух хранилищ.
  • PeekMut::pop позволяет вам вытащить верхний элемент из BinaryHeap<T> после того как вы уже прочитали его без необходимости упорядочивать кучу второй раз.
  • TcpStream::peek, UdpSocket::peek, UdpSocket::peek_from позволяют вам просматривать поток или сокет.

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


Функции Cargo


В Cargo появилась поддержка для Pijul VCS, написанной на Rust.
cargo new my-awesome-project --vcs=pijul включает ее!


В дополнение к флагу --all, Cargo теперь имеет несколько новых флагов, например --bins, --examples, --tests и --benches, которые позволяют вам собирать все программы заданного типа.


Наконец, Cargo теперь поддерживает Haiku и Android!


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


Вклад в 1.18.0


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


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

Поделиться публикацией

Похожие публикации

Комментарии 30

    0

    Хотел как-то посмотреть, как выглядят production-ready проекты на Rust. Похоже, многие проекты, которые можно найти на github, делались для изучения языка и впоследствие забрасывались. Можете порекомендовать что-нибудь production-ready и кем-нибудь реально использующееся на Rust?

      +2

      Видел что часто https://github.com/BurntSushi/ripgrep советуют в ответ на подобное, как пример небольшого, довольно идиоматичного и быстрого кода.


      Есть статейка с обзором кода проекта: http://blog.mbrt.it/2016-12-01-ripgrep-code-review/

        0

        Спасибо за пример проекта, и отдельно — за статью с обзором!

        +1
        Кто использует Rust в production
        В Mozilla Firefox уже часть кода на Rust.
          0

          И правда, с чего я решил что вопрос про исходные коды этих проектов? :(

            –3
            хм! нет, это немного не то, учитывая, что раст возник в мозиллы от потребностей мозиллы. что есть стороннего, независимого?
            то есть суть вопроса — взлетит или нет? го, шарп, цпп, или всё-таки раст? может всё-таки котлин во все поля?
              +3
              имхо, вы сейчас перечислили языки с достаточно сильно разными областями применения, если вам хватает kotlin, то скорее всего вам не нужен rust/cpp
                –2
                завидую такой уверенности в оценках чужих потребностей.
                мне хватает шарпа, ц\цпп и спецязыков на работе и ещё жабы немного. интересен котлин в степени уже бОльшей, чем некоторое время назад был интересен го. а гоу был интересен тем, что на нём достаточно просто создавать лёгкие сервисы с переносимым кодом. в котлин можно уже всё, включая андроид во все поля. можно ли котлин в сервисы с переносимым кодом так же, как в го — пока не понял.
                так что пока смотрю на раст ровно так же, как на го, но знаю при этом, что у го история успеха толще.
                +5
                Вопрос не в популярности сейчас. Пишут, что Dropbox запустил новое хранилище на Rust. OneSignal запустил push нотификацию со 100 000 tps на Rust. Samsung вкладывает в разработчиков компилятора Rust.
                Rust попал в нишу. По мне так Rust надежнее C++ (искать баги проще уже при компиляции) и нет дурацкого GC который есть в Go, Java, C#, Kotlin, D…
                  +1
                  У меня чувство дежавю. Два года назад Dropbox переписывал хранилища на Go, а vk запускали на нём push нотификацию. Go тогда попал в нишу.
                    +3
                    Дело как раз в том, что код хранилища DropBox, написанный на Go, ужасно тормозил. Они его переписали на Rust, и теперь он летает. Они писали об этом в блоге.
                      +1
                      Дело в том, что код Dropbox был написан на python и ужасно тормозил. Они его переписали на Go и стало всё летать. Они писали об этом https://blogs.dropbox.com/tech/2014/07/open-sourcing-our-go-libraries/
                        +3
                        Вот тут лучше описано. Я тоже был не совсем прав — Го жрал слишком много памяти, поэтому переписали на Rust:
                        https://www.wired.com/2016/03/epic-story-dropboxs-exodus-amazon-cloud-empire/
                          +2

                          Справедливости ради, они только пару компонентов переписали, остальная кодовая база у них всё ещё на Го и вроде, говорят, не собираются слезать. Но да, по их словам, памяти сэкономили много.

            0
            > Этот выпуск включает первый черновой вариант второго издания в нашей онлайн документации. 19 из 20 глав уже написаны, черновой вариант 20 главы будет добавлен в выпуске Rust 1.19.

            А есть pdf-версия этой версии?
            +1

            Я собираюсь как-нибудь потыкать Rust, но меня снедают разные сомнения. Вот я хотел спросить об одном — может, кто-нибудь сможет его развеять. Как продвигаются дела с non-lexical borrow? Я просто слышу звон, и у меня есть впечатление, что когда это наконец появится, то стиль кода сильно изменится. Меня это здорово сдерживает — поскольку тыкать собирался не по какой-то необходимости а так, попробовать тёплая ли водичка, то хочется уже сразу писать идиоматично. Вместо этого есть ощущение, что сейчас придётся пробовать poor man's Rust, а уж потом, когда появится non-lexical borrow, вот тогда заживём и наконец я пойму, что такое идиоматичный код. По делу беспокоюсь, или мерещится?

              +2

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

                +1

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


                Этот код не компилируется, так как время жизни возвращаемой ссылки включает весь код функции, в том числе и self.insert(...). То есть компилятор считает, что self менять нельзя даже после return v;, поскольку его часть всё ещё позаимствована возвращаемой ссылкой v.


                fn get<T>(&mut self, key: &T) -> &u32 {
                    if let Some(v) = self.find(key) {
                        return v;
                    }
                    self.insert(key, 0)
                }

                Сейчас советуют использовать два вызова find, что, конечно, менее эффективно:


                fn get<T>(&mut self, key: &T) -> &u32 {
                    if self.find(key).is_none() {
                        return self.insert(key, 0);
                    }
                    self.find(key).unwrap()
                }
                  0
                  Посмотрите, как это делается в стандартной библиотеке:
                  https://doc.rust-lang.org/src/core/option.rs.html#667
                  https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html#method.or_insert
                    +3

                    Entry API как раз и было введено, чтобы решить эту проблему.

                    +1
                    И касательно времени жизни, вам должно помочь оборачивание if-блока в фигурные скобки:
                    fn get<T>(&mut self, key: &T) -> &u32 {
                        {
                            if let Some(v) = self.find(key) {
                                return v;
                            }
                        }
                        self.insert(key, 0)
                    }
                    

                    Не так красиво, но время жизни закончится там, где надо.
                +1
                Очень надеюсь, что у Мозиллы все будет хорошо, либо же кто-то из гигантов заметит язык и начнет использовать и вкладывать в него.
                Через месяц буквально сложности с borrow checker'ом уходят вообще на второй план и даже не задумываешься. Жутко не хватает готовых библиотек, кусков кода. В документации иногда вообще непонятно, что делать с той или иной библиотекой.

                После С++ это как глоток свежего воздуха в этой нише.
                  0

                  А для проектов типа Diesel или Rocket всё ещё нужна Nightly-сборка? Те расширения языка, которые они используют (кодогенерация во все поля) собираются стандартизировать?

                    +2

                    Diesel компилируется с Rust 1.17. Rocket все еще требует nightly. Потихоньку все это стабилизируется (процедурные макросы, например, были стабилизированы то ли в прошлой, то ли в позапрошлой версии)

                      +2
                      процедурные макросы, например, были стабилизированы то ли в прошлой, то ли в позапрошлой версии

                      В 1.15 там же только "custom derive" был стабилизирован. Насколько я помню, полная стабилизация процедурных макросов это еще дело очень далекого будущего.

                        0

                        Но для diesel и serde и эта часть уже вполне хлеб.

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

                  Самое читаемое