- PVSM.RU - https://www.pvsm.ru -

Rust 1.66.0: дискриминанты для перечислений с полями, black_box, cargo remove

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

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

rustup update stable

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

Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать beta (rustup default beta) или nightly (rustup default nightly) канал. Пожалуйста, сообщайте [3] обо всех встреченных вами ошибках.

Что стабилизировано в 1.66.0

Явные дискриминанты для перечислений с полями

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

#[repr(u8)]
enum Foo {
    A(u8),
    B(i8),
    C(bool) = 42,
}

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

#[repr(u8)]
enum Bar {
    A,
    B,
    C = 42,
    D,
}

Здесь перечисление Bar гарантированно будет иметь то же представление в памяти, что и u8. Кроме того, у варианта Bar::C гарантированно есть дискриминант 42. Варианты без явно указанных значений будут иметь дискриминанты, которые автоматически назначаются в соответствии с их порядком в исходном коде, поэтому у Bar::A будет дискриминант 0, у Bar::B будет 1, а у Bar::D будет 43. Без этой функции единственным способом установить явное значение Bar::C было бы добавить 41 ненужный вариант перед ним!

Примечание: в то время как для перечислений без полей можно проверить дискриминант через приведение as (например, Bar::C as u8), Rust не предоставляет способа на уровне языка для доступа к необработанному дискриминанту перечисления с полями. Вместо этого для проверки дискриминанта перечисления с полями необходимо использовать небезопасный в настоящее время код. Поскольку эта функция предназначена для использования с межъязыковым FFI, где уже необходим небезопасный код, мы надеемся, что это не станет слишком большой дополнительной нагрузкой. Пока же если всё, что вам нужно, — это непрозрачный дескриптор дискриминанта, см. функцию std::mem::discriminant.

core::hint::black_box

Когда исследуется или оценивается машинный код, сгенерированный компилятором, бывает полезно отключить оптимизацию для определённых мест. В следующем примере функция push_cap 4 раза вызывает в цикле Vec::push:

fn push_cap(v: &mut Vec<i32>) {
    for i in 0..4 {
        v.push(i);
    }
}

pub fn bench_push() -> Duration {
    let mut v = Vec::with_capacity(4);
    let now = Instant::now();
    push_cap(&mut v);
    now.elapsed()
}

Если вы посмотрите оптимизированный вывод компилятора для x86_64, то заметите, что он выглядит достаточно коротко:

example::bench_push:
  sub rsp, 24
  call qword ptr [rip + std::time::Instant::now@GOTPCREL]
  lea rdi, [rsp + 8]
  mov qword ptr [rsp + 8], rax
  mov dword ptr [rsp + 16], edx
  call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
  add rsp, 24
  ret

Фактически вся функция push_cap, которую мы хотели оценить, была оптимизирована!

Мы можем обойти это при помощи новой стабилизированной функции black_box. Функционально black_box не очень интересна: она лишь берёт значение, которое вы ей передали, и возвращает его обратно. Однако внутри компилятор обрабатывает black_box как функцию, которая что-то делает с передаваемым ей вводом и возвращает какое-то значение (как и следует из её названия).

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

use std::hint::black_box;

fn push_cap(v: &mut Vec<i32>) {
    for i in 0..4 {
        v.push(i);
        black_box(v.as_ptr());
    }
}

Теперь мы можем найти развёрнутый цикл for в нашем оптимизированном ассемблерном выводе [4]:

  mov dword ptr [rbx], 0
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 4], 1
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 8], 2
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 12], 3
  mov qword ptr [rsp + 8], rbx

Вы также можете увидеть побочный эффект вызова black_box в этом ассемблерном выводе. Инструкция mov qword ptr [rsp + 8], rbx бесполезно повторяется после каждой итерации. Эта инструкция записывает адрес v.as_ptr() в качестве первого аргумента функции, которая фактически никогда не вызывается.

Обратите внимание, что сгенерированный код вообще не связан с возможностью распределения памяти, введённого вызовом push. Это потому, что компилятор всё ещё опирается на тот факт, что мы вызвали Vec::with_capacity(4) в функции bench_push. Вы можете поиграть с размещением black_box или попробовать использовать его в нескольких местах, чтобы увидеть его влияние на оптимизацию компилятора.

cargo remove

Rust 1.62.0 представил cargo add — утилиту командной строки для добавления зависимостей в ваш проект. В этой же версии вы сможете использовать cargo remove для их удаления.

Стабилизированные API

Прочие изменения

Выпуск Rust 1.66 включает и другие изменения:

  • Теперь вы можете использовать в шаблонах диапазоны ..=X.
  • Linux-сборки теперь используют оптимизированные rustc фронтенд и LLVM бэкенд с LTO и BOLT соответственно, что оптимизирует быстродействие и использование памяти.

Проверьте всё, что изменилось в Rust [2], Cargo [17] и Clippy [18].

Участники 1.66.0

Многие люди собрались вместе, чтобы создать Rust 1.66.0. Без вас мы бы не справились. Спасибо! [19]

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

С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате [20] или же в аналогичном чате для новичковых вопросов [21]. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков [22].

Данную статью совместными усилиями перевели andreevlex [23], TelegaOvoshey [24] и funkill [25].

Автор:
RustLangRu

Источник [26]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/381405

Ссылки в тексте:

[1] страницы: https://www.rust-lang.org/install.html

[2] подробным описанием выпуска 1.66.0: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1660-2022-12-15

[3] сообщайте: https://github.com/rust-lang/rust/issues/new/choose

[4] оптимизированном ассемблерном выводе: https://rust.godbolt.org/z/Ws1GGbY6Y

[5] proc_macro::Span::source_text: https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.source_text

[6] u*::{checked_add_signed, overflowing_add_signed, saturating_add_signed, wrapping_add_signed}: https://doc.rust-lang.org/stable/std/primitive.u8.html#method.checked_add_signed

[7] i*::{checked_add_unsigned, overflowing_add_unsigned, saturating_add_unsigned, wrapping_add_unsigned}: https://doc.rust-lang.org/stable/std/primitive.i8.html#method.checked_add_unsigned

[8] i*::{checked_sub_unsigned, overflowing_sub_unsigned, saturating_sub_unsigned, wrapping_sub_unsigned}: https://doc.rust-lang.org/stable/std/primitive.i8.html#method.checked_sub_unsigned

[9] BTreeSet::{first, last, pop_first, pop_last}: https://doc.rust-lang.org/stable/std/collections/struct.BTreeSet.html#method.first

[10] BTreeMap::{first_key_value, last_key_value, first_entry, last_entry, pop_first, pop_last}: https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html#method.first_key_value

[11] Добавлена реализация AsFd для типов блокировок стандартных потоков для WASI: https://github.com/rust-lang/rust/pull/101768/

[12] impl TryFrom<Vec<T>> for Box<[T; N]>: https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#impl-TryFrom%3CVec%3CT%2C%20Global%3E%3E-for-Box%3C%5BT%3B%20N%5D%2C%20Global%3E

[13] core::hint::black_box: https://doc.rust-lang.org/stable/std/hint/fn.black_box.html

[14] Duration::try_from_secs_{f32,f64}: https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.try_from_secs_f32

[15] Option::unzip: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.unzip

[16] std::os::fd: https://doc.rust-lang.org/stable/std/os/fd/index.html

[17] Cargo: https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-166-2022-12-15

[18] Clippy: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#rust-166

[19] Спасибо!: https://thanks.rust-lang.org/rust/1.66.0/

[20] русскоязычном Телеграм-чате: https://t.me/rustlang_ru

[21] чате для новичковых вопросов: https://t.me/rust_beginners_ru

[22] чат переводчиков: https://t.me/rustlang_ru_translations

[23] andreevlex: https://habr.com/ru/users/andreevlex/

[24] TelegaOvoshey: https://habr.com/ru/users/telegaovoshey/

[25] funkill: https://habr.com/ru/users/funkill/

[26] Источник: https://habr.com/ru/post/706176/?utm_source=habrahabr&utm_medium=rss&utm_campaign=706176