Выпуск Rust 1.39.0: async/await, аттрибуты для параметров функций, новые константные функции

Автор оригинала: The Rust Release Team
  • Перевод

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


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


$ rustup update stable

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


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


Наиболее существенные нововведения включают в себя синтаксис async/.await, разделяемые ссылки к перемещаемым значениям в match-guards и атрибуты у параметров функций. Смотрите подробные примечания к выпуску для дополнительной информации.


С .await закончено, встречайте async fn


Ранее в Rust 1.36.0 мы объявили о доступности трейта Future. Тогда же мы отметили, что:


Мы надеемся, что это нововведение позволит популярным крейтам, библиотекам и в целом всей экосистеме подготовиться к синтаксису async/.await, стабилизация которого планируется на недалёкое будущее.

Обещание дано — обещание выполнено. Мы рады объявить, что долгожданный синтаксис async/.await, позволяющий определять асинхронные функции и блоки (async) и ожидать их выполнения (.await), наконец стабилизирован!


Асинхронная функция, определяемая посредством синтаксиса async fn (вместо обычного fn), ничего не делает кроме того, что при вызове возвращает объект, реализующий трейт Future. Данный объект является приостановленным вычислением, завершить которое можно синтаксисом .await. Кроме того, async fn, async { ... } и async move { ... } действуют как замыкания и могут использоваться для определения асинхронных литералов.


Подробнее о выпуске async/.await можете почитать в посте блога Niko Matsakis.


Разделяемые ссылки к перемещаемым значениям в match-guards


При сопоставлении с образом переменную, известную как "байндинг" (binding), можно привязать одним из следующих способов:


  • По ссылке, неизменной или изменяемой. Это можно сделать явно, например, синтаксисом ref my_var или ref mut my_var соответственно. Почти всегда режим привязки будет выведен автоматически.


  • По значению — либо копированием (если тип привязанной переменной реализует трейт Copy), либо передачей во владение.



Теперь разрешено использовать переменные шаблона в if-ограничениях шаблона, если владение передано в эту переменную (т.е. переменная в шаблоне binds-by-move). Ранее следующий код был бы отвергнут:


fn main() {
    let array: Box<[u8; 4]> = Box::new([1, 2, 3, 4]);

    match array {
        nums
//      ---- `nums` привязан с владением (by move).
            if nums.iter().sum::<u8>() == 10
//                 ^------ `.iter()` берется явная ссылка на `nums`
        => {
            drop(nums);
//          ----------- `nums` привязан и в нашем владении
        }
        _ => unreachable!(),
    }
}

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


Атрибуты для параметров функций


Начиная с Rust 1.39.0, атрибуты можно применять для параметров функций, замыканий и указателей на функции. Раньше функция len() могла быть представлена следующим образом:


#[cfg(windows)]
fn len(slice: &[u16]) -> usize {
    slice.len()
}
#[cfg(not(windows))] 
fn len(slice: &[u8]) -> usize {
    slice.len()
}

Но Rust 1.39.0 позволяет написать приведённый выше код значительно проще:


fn len(
    #[cfg(windows)] slice: &[u16], // Этот параметр будет использован в операционной системе Windows.
    #[cfg(not(windows))] slice: &[u8], // В остальных случая будет использован этот параметр.
) -> usize {
    slice.len()
}

Атрибуты, пригодные к использованию, включают в себя:


  1. Условную компиляцию: cfg и cfg_attr;


  2. Управление проверками: allow, warn, deny и forbid;


  3. Вспомогательные атрибуты, используемые процедурными макросами-атрибутами для элементов синтаксиса.


    Надеемся, что данное нововведение поможет экосистеме в написании более выразительных DSL, основанных на макросах.



Миграционные предупреждения проверки заимствований становятся серьёзными ошибками в Rust 2018


В релизе 1.35.0 мы анонсировали, что новый анализатор заимствований (NLL) пришёл в редакцию Rust 2015 после того, как был выпущен для редакции Rust 2018 в версии 1.31.


Как указано в релизе 1.35.0, предыдущий анализатор заимствований был спроектирован с ошибками, допускавшими небезопасное использование памяти. Эти ошибки были исправлены с помощью нового анализатора, реализующего NLL. Так как эти исправления могли сломать работающий код, команда Rust решила вводить их постепенно, проверяя, что предыдущий анализатор должен принять код, который новый анализатор отклонит. Если это так, то ошибки вместо этого пока станут предупреждениями.


Начиная с Rust 1.39.0, эти предупреждения теперь являются ошибками в редакции Rust 2018.
В следующем релизе Rust 1.40.0, данное правило также будет применятся к коду редакции Rust 2015, что позволит полностью устранить старый анализатор заимствований из компилятора.


Если вас затронули эти изменения, или вы хотите узнать больше, почитайте пост в блоге Niko Matsakis.


Больше константных функций в стандартной библиотеке


Начиная с Rust 1.39.0, следующие функции помечены как константные (const fn):



Стабилизированные функции в стандартной библиотеке


В Rust 1.39.0 были стабилизированы следующие функции:



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


Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения.


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


Участники 1.39.0


Множество людей собрались вместе, чтобы создать Rust 1.39.0. Мы не смогли бы сделать это без всех вас, спасибо!


От переводчиков


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


Данную статью совместными усилиями перевели andreevlex, blandger, funkill и Hippolot.

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    +1

    А тем временем по поводу async-std vs tokio во всю не очень понятная драма развивается.
    https://www.reddit.com/r/rust/comments/dse875/2020_be_open_and_friendly/
    Причем эмоции прямо зашкаливают

      +1
      Бедный humbug, забанили. Хнык
        +3

        Но вообще мне в этой истории непонятны нападки на async-std, что они создают фрагментацию и страдают NIH синдромом. В конце концов их видение очень сильно отличается от видения Tokio и так с ходу не очевидно, чье же видение в реальности лучше.
        И кажется мне, что в таких делах все точки над i расставит время и реальные истории использования.
        В конце концов это не первая и не последняя история с войной форков в опенсорсе и многие прошлые войны в результате привели к созданию очень годных продуктов.

          0
          Если честно, то я пока из реддита не понял точных причин происходящего, еще дочитываю. Было бы интересно.

          Просто идентифицировал пострадавшего.

          Кстати на реддите модераторы прикрывают этот флейм.
          This thread has been locked by the moderators of r/rust
          И блочат (похоже что и на гите o_O) всех комментирующих. Мило =)
            0
            Ну я так понимаю все началось из-за этого:

            github.com/withoutboats/romio/pull/106

            humbug с помощью ряда верных падаванов попытался пропиарить tokio как альтернативу async-std в некоем богом забытом проекте, некоторые участники этого проекта (в том числе его автор) несколько удивились явному форсингу рядового коммита по правке документации, и тут заверте…
              0
              Похоже на то, что их старую (3х летнюю) разработку токио бортанули в пользу нового переписанного на только введенных асинках асинк-рс, которой нет и 3х месяцев
                +1
                Ну вроде не то чтобы бортанули, просто не упомянули как альтернативу (правда в конце концов таки упомянули вкратце, но остальные PR-related правки withoutboats вносить не стал).
              +3

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

              0
              Было бы интересно сравнить удобства async/await с использованием монад. Но в Rust отказались добавлять HKT, а следовательно и монады.
                0

                Не то чтобы отказались совсем, через GAT можно делать HKT как я понимаю. Но вот пока GAT не осилили

              +1
              В данной ситуации мне не нравится, что на достаточно высоком уровне происходит конфликт, который (уже сейчас) выливается в стычки, задевающие непричастных людей. Не опускаясь до обсуждения «токсичности», «недружелюбности» и прочих упоминаемых терминов, мне кажется, что такое явление потенциально может негативно сказаться на сообществе в целом, вызвав раскол и снизив тем самым синергию.
                +1

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

                  0
                  Да даже если и было, если и прыскать ядом — то в направлении обидевшего, а не на всех вокруг.
              +1
              А можно какое-нибудь summary драмы в нескольких предложениях?
              +12

              Я забросил писать русскоязычные хабро-ежемесячники про раст, зато, если кому интересно, пару месяцев назад стал писать официальные ежемесячники чисто про ржавую разработку игр — https://rust-gamedev.github.io — только что вот октябрьский выпуск опубликован. :)

                0
                Спасибо за ваши ежедневники. С играми у вас и вправду нажористее получается. Хотелось бы и на хабре на русском эти статьи видеть.
                  0

                  Про просто кросспостинг еще можно подумать (вроде, хабр к этому теперь спокойней относится), а вот именно переводить дайджест, тем более довольно нишевый, мне не кажется, что стоит усилий — все ж таки там не лонгрид, а только названия с картинками + пара предложений описания.

                0
                Какой-то странный асинк/авэйт. Вроде сделали, но сделали ленивым, асинхронная функция не начинает исполняться до авэйта. Получается, что невозможно запустить 2 асинхронные функции параллельно, код
                let af = get_a_async();
                let bf = get_b_async();
                let a = af.await;
                let b = bf.await;
                
                не приведёт к поочерёдному исполнению частей get_a_async и get_b_async. Вообще разделение на 2 стадии, вызов функции и ожидание теряет смысл, можно было бы await заменить на вызов функции.
                  +3

                  Сам async/.await тут ни при чём. Просто в Rust и без async/.await модель исполнения futures сделана poll-based, а не push-based как к этому все привыкли в других языка.
                  Это был осознанный выбор и этому есть ряд причин, которые неплохо описаны в этим статьях:



                  Что же касается "невозможностей", то таких нет. Всё возможно, просто выполняется немного иначе, нежели привыкли, и с оглядкой на poll-based ленивость футур. Конкретно ваш пример решается следующим образом:


                  let (a, b) = futures::join!(get_a_async(), get_b_async()).await;
                    0

                    А почему join макрос? Разве функцией он не реализуется?

                      +2

                      В Rust нет variadic function arguments, потому существует несколько вариантов join'а в зависимости от кол-ва аргументов: join, join3, join4,… join_all.
                      Как вы догадываетесь, это обилие было бы не слишком приятно видеть в коде, потому эти функции сверху прикрыты макросом join!, который при разворачивании автоматически использует необходимую функцию.
                      Собственно, использование макроса для того, чтобы предоставить возможности variadic arguments — достаточно стандартный приём в Rust и применяется много где.

                        0

                        Спасибо, понятно. Я так понимаю, что variadic generics есть в долгосрочных планах и они заменят в будущем такого рода использование макросов?

                          0

                          Я ничего не слышал про планы на variadic generics. В плане языковых фич в данный момент есть планы на const generics, GATs (generic associated types) и specialization.


                          Но, даже если бы variadic generics были, я сомневаюсь, чтобы они помогли в данной ситуации, ведь они оперируют на уровне сигнатуры типов, а не сигнатуры аргументов функции. Вещи, конечно же, связанные, но далеко не одно и то же.


                          А в чём проблема использования макросов? ИМХО, одна из положительных сторон Rust — это как раз first-class citizen макросы. Они очень гибки и очень помогают для решения часто самых неожиданных задач, гораздо более чаще, нежели затыкают "дыры" выразительности языковых конструкций. После Rust их часто очень не хватает в других языках.

                            0
                            Я ничего не слышал про планы на variadic generics.
                            Я просто загуглил и нашел такой pre-RFC. Возможно, дальше этого дело не пошло, я не в курсе.
                            Но, даже если бы variadic generics были, я сомневаюсь, чтобы они помогли в данной ситуации, ведь они оперируют на уровне сигнатуры типов, а не сигнатуры аргументов функции.
                            Ну если variadic generics случатся, то написать функцию принимающую переменное число аргументов не должно быть проблемой (сужу по опыту C++).
                            А в чём проблема использования макросов?
                            Наверное, в использовании нет проблем (у меня нет практического опыта на Rust-е). Я думал про написание такого макроса — оперировать потоком токенов должно быть сложнее, чем написание generic функции.
                              0
                              Я думал про написание такого макроса — оперировать потоком токенов должно быть сложнее, чем написание generic функции.

                              С этим в целом глупо спорить. Но хорошая новость в том, что в этом Rust тоже достаточно не плох:


                              macro_rules! map {
                                  ($( $key:expr => $val:expr ),*) => {{
                                      let mut hm = HashMap::new();
                                      $( hm.insert($key, $val); )*
                                      hm
                                  }};
                              }
                              
                              let number_names = map! {
                                  1 => "one",
                                  2 => "two"
                              };

                              Playground


                              Как видим, не ядерная физика в тривиальных случаях =)
                              В остальных будет больнее, да.

                                0

                                Да и эта * похожа на… в си плюс плюс, но когда надо целый класс определить с variadic, то как хорошо IDE умеет работать с кодом внутри макроса?

                                  0

                                  Пока что с IDE, и в особенности их работой с макросами, всё далеко не сладко, увы.

                                    0
                                    Вроде хвалили плагин к CLion, не пробовали?
                                      +1

                                      Им и живы =)
                                      Но там тоже все ещё много работы в плане макросов. Декларативные раскрываются хорошо и дают нормальный автокомплит, но вот редактирование синтаксиса внутри декларативного макроса — только руками. У процедурных все не очень в плане раскрытия, но вот их написание вполне ОК, так как это обычный код работающий с токенами и AST.

                            –1
                            Я не очень понимаю, зачем он нужен, когда есть макросы. Фиксированное количество аргументов позволяет лучше оптимизировать вызовы функций.
                    0
                    del
                      0

                      А кириллические / не латинские имена переменных и функций разрешили использовать? В предыдущей версии Rust обещали разрешить "потом" :(

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

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