Rust 1.87.0 и 10 лет Rust!


Команда Rust празднует 10-летие Rust в Утрехте, Нидерланды, и рада сообщить о новой версии языка — 1.87.0!



Сегодняшний день релиза выпал на 10-летний юбилей выхода Rust 1.0!


Спасибо мириадам участников, кто работал или работает над Rust. Выпьем за ещё многие десятилетия впереди! ?




Как обычно, новая версия включает в себя все изменения, которые были внесены в бета-версию за последние шесть недель согласно последовательному и регулярному циклу выпуска. Мы следуем ему начиная с Rust 1.0.


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


$ rustup update stable

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


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


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


Анонимные конвейеры


1.87 даёт доступ из стандартной библиотеки к анонимным каналам, включая интеграцию с методами ввода/вывода std::process::Command. Например, объединить stdout и stderr в один поток теперь относительно просто, в то время как раньше необходимо было использовать разные потоки или платформо-специфические функции.


use std::process::Command;
use std::io::Read;

let (mut recv, send) = std::io::pipe()?;

let mut command = Command::new("path/to/bin")
    // И stdout, и stderr теперь пишут в один канал.
    .stdout(send.try_clone()?)
    .stderr(send)
    .spawn()?;

let mut output = Vec::new();
recv.read_to_end(&mut output)?;

// Обратите внимание, что что мы читаем из канала до завершения процесса, для исключения
// переполнения буфера ОС, если программа создаст очень много вывода.
assert!(command.wait()?.success());

Безопасные архитектурные интринсики


Большинство интринсиков из std::arch, которые небезопасны только из-за того, что требуют включения таргет-фич, теперь могут быть вызваны из безопасного кода, в котором эти фичи включены. Например, следующая программа, реализующая суммирование элементов массива с использованием ручных интринсиков, теперь использует безопасный код в основном цикле.


#![forbid(unsafe_op_in_unsafe_fn)]

use std::arch::x86_64::*;

fn sum(slice: &[u32]) -> u32 {
    #[cfg(target_arch = "x86_64")]
    {
        if is_x86_feature_detected!("avx2") {
            // БЕЗОПАСНОСТЬ: Во время работы мы удостоверились, что необходимая функциональность присутствует,
            // так что вызывать эту функицю безопасно.
            return unsafe { sum_avx2(slice) };
        }
    }

    slice.iter().sum()
}

#[target_feature(enable = "avx2")]
#[cfg(target_arch = "x86_64")]
fn sum_avx2(slice: &[u32]) -> u32 {
    // БЕЗОПАСНОСТЬ: __m256i и u32 одинаково валидны.
    let (prefix, middle, tail) = unsafe { slice.align_to::<__m256i>() };

    let mut sum = prefix.iter().sum::<u32>();
    sum += tail.iter().sum::<u32>();

    // Основной цикл теперь полностью состоит из безопасного кода, потому что интринсики, требующие таргет фичу (avx2),
    // упакованы в саму функцию.
    let mut base = _mm256_setzero_si256();
    for e in middle.iter() {
        base = _mm256_add_epi32(base, *e);
    }

    // БЕЗОПАСНОСТЬ: __m256i и u32 одинаково валидны.
    let base: [u32; 8] = unsafe { std::mem::transmute(base) };
    sum += base.iter().sum::<u32>();

    sum
}

asm! прыжки в Rust-код


Встроенный ассемблер (asm!) теперь может прыгать в помеченные участки Rust-кода. Это делает более гибким низкоуровневое программирование — например, реализацию оптимизированного контроля управления в ядрах ОС или более эффективное взаимодействие с железом.


  • Макрос asm! теперь поддерживает операнд label, который работает как переход к метке
  • Метка должна быть блочным выражением с возвращаемым типом () или !
  • Блок выполняется, когда на него совершается прыжок. Выполнение продолжается после блока asm!.
  • Использование операндов output и label в одном вызове asm! остаётся unstable.

unsafe {
    asm!(
        "jmp {}",
        label {
            println!("Выскочил из asm!");
        }
    );
}

Больше информации можно найти в reference.


Прецизионный захват (+ use<...>) в impl Trait в объявлении трейтов


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


Несколько примеров:


trait Foo {
    fn method<'a>(&'a self) -> impl Sized;

    // ... преобразуется во что-то вроде:
    type Implicit1<'a>: Sized;
    fn method_desugared<'a>(&'a self) -> Self::Implicit1<'a>;

    // ... а при прецезионном захвате ...
    fn precise<'a>(&'a self) -> impl Sized + use<Self>;

    // ... преобразуется во что-то подобное:
    type Implicit2: Sized;
    fn precise_desugared<'a>(&'a self) -> Self::Implicit2;
}

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



Следующие API теперь можно использовать в контексте const:



Удаление таргета i586-pc-windows-msvc


Таргет i586-pc-windows-msvc удалён из Tier 2. Отличие i586-pc-windows-msvc от более популярного таргета i686-pc-windows-msvc из Tier 1 в том, что i586-pc-windows-msvc не требует поддержки инструкций SSE2. Но Windows 10, минимально допустимая версия ОС для всех windows-таргетов (за исключением win7), сама по себе требует инструкций SSE2.


Все пользователи, использующие в качестве целевой платформы i586-pc-windows-msvc, должны мигрировать на i686-pc-windows-msvc.


Для большей информации вы можете изучить Major Change Proposal.


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


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


Кто работал над 1.87.0


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


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


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

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


  1. AbitLogic
    17.05.2025 16:00

    Обновили бы русский rust book, а то там люди до сих пор листают 1.67


  1. ptr128
    17.05.2025 16:00

    Я уже теряю надежду увидеть в Rust стабильный ABI, вместо костыля abi_stable с extern "C".

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

    Уже молчу о стандарте языка, который всё более востребован в связи все с более частой необходимостью использовать GCC или форки rustc для компиляции под RISC-V или иные платформы, для которых LLVM по ряду причин не имеет стабильного кодогенератора.


    1. AbitLogic
      17.05.2025 16:00

      А можно поподробнее чего там llvm не имеет, мы применяем для ARM, пока никаких проблем не встречали, чего там не так с risc-v? А то может пора следить за gccrs, а мужики то и не знают


      1. ptr128
        17.05.2025 16:00

        А можно поподробнее чего там llvm не имеет, мы применяем для ARM, пока никаких проблем не встречали

        С ARM у LLVM всё хорошо, за исключением поддержки сопроцессоров. Но это отдельная история. Тот же PIO в RP2040 можно и на ассемблере программировать. С Integer Divider в RP2040 ситуация хуже, так как совершенно непонятно, как в Rust отсчитать восемь тактов.

        Куда веселее, когда в CPU/SoC ядра и ARM, и RISC-V.

        чего там не так с risc-v?

        Если упрощенно, то или поддержка экспериментальная (как у E), или через ассемблер (как у H и Q).

        Если копнуть глубже, то есть проблемы с оптимизацией. Например, IR в LLVM хорошо ложится на флаги (ZNCV), которые есть в ARM и amd64, но которых в принципе нет в RISC-V.

        Если упрощенно, то выражение if a > b { ... } else { ... } ; при целочисленных a и b для IR две команды, а для RISC-V - одна.

        По этой причине, например, Milk-V предлагают GCC toolchain.

        Для Espressif тоже есть тонкости. Требуется:

        • Espressif Rust fork with support for Espressif targets

        • nightly toolchain with support for RISC-V targets

        • LLVM fork with support for Xtensa targets

        • GCC toolchain that links the final binary

        может пора следить за gccrs

        Они неизбежно отстают от rustc, разработчики которого считают rustc референсной реализацией Rust, что особенно заметно при попытке использовать std library. В случае no_std явных проблем нет.

        Например, ESP32-C5 rustc вообще пока не поддерживается и судьба его неясна, так как в LLVM поддержки Xhwlp нет даже в планах, а Zcmt поддерживается только ассемблером. При этом Espressif кодогенератор GCC для него уже опубликовал, следовательно gccrs должен работать. Но я не пробовал, так как ESP32-C5 официально стал доступен только с 1 мая 2025 года.