Выпуск Rust 1.32

https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html
  • Перевод

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


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


$ rustup update stable

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


Небольшое замечание: за последнее время вышло несколько новых выпусков rustup! Чтобы обновить сам rustup, выполните rustup self update.

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


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


Макрос dbg


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


let x = 5;

println!("{:?}", x);

// или даже так
println!("{:#?}", x);

Это не самое большое препятствие, замедляющее разработку, но это требует чересчур много усилий для простого отладочного вывода значения x. Кроме того, тут не учитывается контекст. Если у вас есть несколько таких println!-ов, то становится сложно определить, что к чему относится в выводе, пока вы сами не добавите информацию о контексте к каждому вызову, что требует еще больше работы.


Для этих целей в Rust 1.32.0 мы добавили новый макрос dbg!:


fn main() {
    let x = 5;

    dbg!(x);
}

После запуска этой программы, вы увидите:


[src/main.rs:4] x = 5

Вместе с именем переменной и ее значением будет выведено название файла и номер строки, где был совершен вызов dbg!.


Кроме того, println! печатает в стандартный поток вывода, поэтому лучше использовать eprintln! для печати в стандартный поток ошибок. Макрос dbg! выводит в stderr, и это правильно.


Это работает даже в сложных случаях. Рассмотрим пример реализации факториала:


fn factorial(n: u32) -> u32 {
    if n <= 1 {
        n
    } else {
        n * factorial(n - 1)
    }
}

Для того, чтобы его отладить, мы можем использовать eprintln!:


fn factorial(n: u32) -> u32 {
    eprintln!("n: {}", n);

    if n <= 1 {
        eprintln!("n <= 1");

        n
    } else {
        let n = n * factorial(n - 1);

        eprintln!("n: {}", n);

        n
    }
}

Мы хотим выводить n на каждой итерации и видеть контекст каждой из веток. Для factorial(4) будет выведено:


n: 4
n: 3
n: 2
n: 1
n <= 1
n: 2
n: 6
n: 24

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


Рассмотрим тот же пример с использованием dbg!:


fn factorial(n: u32) -> u32 {
    if dbg!(n <= 1) {
        dbg!(1)
    } else {
        dbg!(n * factorial(n - 1))
    }
}

Мы просто обернули макросом каждое из выражений, которое мы хотим вывести. В результате мы получим:


[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = true
[src/main.rs:4] 1 = 1
[src/main.rs:5] n * factorial(n - 1) = 2
[src/main.rs:5] n * factorial(n - 1) = 6
[src/main.rs:5] n * factorial(n - 1) = 24
[src/main.rs:11] factorial(4) = 24

Так как макрос dbg! возвращает само отлаживаемое значение, в отличии от eprintln!, который возвращает (), то нам не нужно делать никаких изменений в структуре нашего кода. Дополнительно мы получаем гораздо более полезный вывод.


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


По умолчанию убран jemalloc


Давным-давно у Rust был большой рантайм, подобный Erlang'овому. Для него был выбран jemalloc вместо системного аллокатора, потому что он часто был производительнее. Постепенно мы все больше и больше избавлялись от рантайма и в конце-концов он почти весь был удален, но jemalloc остался. У нас не было способа выбирать пользовательский аллокатор, и поэтому мы не могли окончательно убрать jemalloc, чтобы не навредить тем, кто нуждался в нем.


Кроме того, утверждение, что jemalloc был всегда аллокатором по умолчанию, относилось преимущественно к UNIX-миру, так как он был по умолчанию только на некоторых платформах. В частности цель MSVC на Windows уже давно использует системный аллокатор.


Наконец, хотя jemalloc обычно имеет хорошую производительность, это не всегда так. К тому же он добавляет около 300 килобайт к каждому исполняемому файлу. Также у нас накопилось множество других проблем с jemalloc. И вообще, это странно, что системный язык по умолчанию не использует системный аллокатор.


По этим причинам, как только Rust 1.28 предоставил способ выбирать глобальный аллокатор, мы начали планировать переход на системный аллокатор по умолчанию и предоставление jemalloc в качестве внешней библиотеки. В Rust 1.32 мы наконец завершили эту работу, и теперь по умолчанию ваша программа будет использовать системный аллокатор.


Если вы хотите продолжать использовать jemalloc, используйте библиотеку jemallocator. Для этого в Cargo.toml укажите:


jemallocator = "0.1.8"

И в корневом файле вашего проекта:


#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

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


Заключительные улучшения системы модулей


В последних двух выпусках мы говорили о нескольких улучшениях системы модулей. В 1.32.0 и редакцию 2018 мы добавили последнее изменение. Оно называется "единообразные пути" ("uniform paths") и разрешает работать с путями импорта также, как с остальными путями, что до этого работало некорректно. Например:


enum Color { Red, Green, Blue }

use Color::*;

Этот код ранее не компилировался, поскольку пути в use должны были начинаться с super, self или crate. Теперь, благодаря поддержке компилятором единообразных путей, этот код будет работать и сделает то, что вы ожидаете: импорт вариантов определенного выше перечисления Color.


Этим изменением завершается наша ревизия системы модулей. Мы надеемся, что вы получите удовольствие от работы с упрощенной системой!


Улучшения макросов


В Rust 1.32.0 выпущено несколько улучшений макросов. Во-первых, был добавлен новый спецификатор фрагмента literal:


macro_rules! m {
    ($lt:literal) => {};
}

fn main() {
    m!("some string literal");
}

Фрагмент literal сопоставляется литералам любого типа: строковым, числовым и символьным.


В редакции 2018 в макросе macro_rules можно использовать также и ?:


macro_rules! bar {
    ($(a)?) => {}
}

Фрагменту с ? будет сопоставлено ноль или одно вхождение, подобно тому, как фрагменту с * уже сопоставляется "ноль или более" вхождений, а с + — "одно или более" вхождений.


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


Макрос dbg!, о котором мы уже рассказали выше, стал важным добавлением к стандартной библиотеке. Помимо этого, 19 функций были сделаны константными и все числовые примитивные типы получили функции преобразования в байтовый массив и обратно с указанным порядком байтов. Всего шесть функций с именами to_<endian>_bytes и from_<endian>_bytes, где <endian> это:


  • ne — нативный порядок (native endianness)
  • le — порядок от младшего к старшему (little endian)
  • be — порядок от старшего к младшему (big endian)

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


Улучшения в Cargo


Cargo получил псевдоним cargo c для команды cargo check, и теперь разрешает использовать имена пользователей в URL репозиториев.


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


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


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


От переводчика: выражаю отдельную благодарность участникам сообщества Rustycrate и лично @dashadee и ozkriff за помощь с переводом и вычиткой.

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

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

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

    –4
    Что с поддержкой ООП в новой версии?
      +9
      Также, как и раньше — ее нет )
        +3

        Конкретно в этой версии ничего особо связанного (и в ближайших следующих ничего не предвидется), но в новой редакции книги есть глава про этот вопрос: http://rustbook.ru/ch17-00-oop.html


        А в целом ответ классический — ооп в мэйнстримовом понимании термина нет, задачи решаются другими приемами.

          +2
          Чуть лучше чем в Go (нет двух разных nil и прочих приколов). Значительно лучше чем в Си. Но хуже чем в объектно-ориентированных языках.
          +6

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

            0
            А мне ненравится. Они недогадались, чтобы можно было переменные через запятую перечислить.
            В результате, вроде он и есть — но не очень нужен, хотя сама идея была хорошей.
              +3

              Как-то слишком категорично, тем более что:
              1) такое изменение можно внести потом, потому что оно обратно-совместимо;
              2) ценой одной пары скобок это уже можно делать через кортеж:


              fn main() {
                  let a = 0_u8;
                  let b = a + 1;
                  let c = "no";
                  dbg!((a, b, c));
              }

                 Compiling playground v0.0.1 (/playground)
                  Finished dev [unoptimized + debuginfo] target(s) in 0.63s
                   Running `target/debug/playground`
              [src/main.rs:5] (a, b, c) = (
                  0,
                  1,
                  "no"
              )

              Playground

                0
                Этот костыль работает, но логичней было бы видеть вывод какой-то такой:

                [src/main.rs:5] a=0, b=1, c=«no»


                а то сопоставлять переменные со значением неудобно.
                  +1

                  Но тогда будет костыль с тем, что в этом специальном случае вызова "dbg!" он внезапно будет возвращать или (), или втихоря паковать перменные в кортеж.
                  Если возможность прозрачной вставки в середину выражения не нужна, то, кмк, проще ак и раньше "eprintln!" использовать с какой угодно форматной строкой:


                  eprintln!("a={}, b={}, c={}", a, b, c);

                    0
                    Меня бы устроило, чтобы он возвращал () в варианте, когда в него передается несколько переменных.
                    +3

                    Если сильно надо, то такое довольно просто реализуется:


                    macro_rules! dbgs {
                        ($val:expr) => {
                            dbg!($val)
                        };
                        ($($val:expr),+) => {
                            eprint!("[{}:{}]", file!(), line!());
                            dbgs!(@dbgs $($val),+);
                            eprintln!();
                        };
                        (@dbgs $current:expr, $($val:expr),+) => {
                            eprint!(" {} = {:#?},", stringify!($current), $current);
                            dbgs!(@dbgs $($val),+);
                        };
                        (@dbgs $last:expr) => {
                            eprint!(" {} = {:#?}", stringify!($last), $last);
                        }
                    }
                    ...
                    let a = 2;
                    let b = 3;
                    
                    dbgs!(a + b);
                    dbgs!(a, b, a + b);

                    После запуска:


                    [src/main.rs:23] a + b = 5
                    [src/main.rs:24] a = 2, b = 3, a + b = 5
                      0
                      Да, и тем больней осознавать, что такого нет в стандартной либе. Делается просто, нужно всем — а в либу не включили.
                        0
                        Можете открыть issue.
                  +1
                  А что макрос должен возвращать, если ему на вход попали две перечисленные через запятую переменные?
                    0
                    а зачем ему вообще что-то возвращать? пускай хотя бы void или по-растовскому () возвращает.
                      +1

                      Извините, а вы обсуждаемый пост читали?


                      Один из сценариев использования макроса dbg! — внедрение его внутрь вычисляемого выражения без изменения остальных частей кода.

                        0
                        Такого, чтобы внедряли и внутри было несколько переменных через запятую — не могу представить. Хотелось бы увидеть пример.
                  +1

                  Как минимум офигенная штука для отладки кода в плейпене. Я (как и многие растовики) часто там код пишу, а нормального отладчика там все равно нет и вряд ли имеет смысл заморачиваться с втаскиванием.

                  0

                  Появилась возможность использовать русские/не латинские имена переменных и функций? Вроде в сообществе было обсуждение этой темы и собирались сделать...

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

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