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
Vec::extract_if
vec::ExtractIf
LinkedList::extract_if
linked_list::ExtractIf
<[T]>::split_off
<[T]>::split_off_mut
<[T]>::split_off_first
<[T]>::split_off_first_mut
<[T]>::split_off_last
<[T]>::split_off_last_mut
String::extend_from_within
os_str::Display
OsString::display
OsStr::display
io::pipe
io::PipeReader
io::PipeWriter
impl From<PipeReader> for OwnedHandle
impl From<PipeWriter> for OwnedHandle
impl From<PipeReader> for Stdio
impl From<PipeWriter> for Stdio
impl From<PipeReader> for OwnedFd
impl From<PipeWriter> for OwnedFd
Box<MaybeUninit<T>>::write
impl TryFrom<Vec<u8>> for String
<*const T>::offset_from_unsigned
<*const T>::byte_offset_from_unsigned
<*mut T>::offset_from_unsigned
<*mut T>::byte_offset_from_unsigned
NonNull::offset_from_unsigned
NonNull::byte_offset_from_unsigned
<uN>::cast_signed
NonZero::<uN>::cast_signed
<iN>::cast_unsigned
NonZero::<iN>::cast_unsigned
<uN>::is_multiple_of
<uN>::unbounded_shl
<uN>::unbounded_shr
<iN>::unbounded_shl
<iN>::unbounded_shr
<iN>::midpoint
<str>::from_utf8
<str>::from_utf8_mut
<str>::from_utf8_unchecked
<str>::from_utf8_unchecked_mut
Следующие API теперь можно использовать в контексте const
:
core::str::from_utf8_mut
<[T]>::copy_from_slice
SocketAddr::set_ip
-
SocketAddr::set_port
, SocketAddrV4::set_ip
-
SocketAddrV4::set_port
, SocketAddrV6::set_ip
SocketAddrV6::set_port
SocketAddrV6::set_flowinfo
SocketAddrV6::set_scope_id
char::is_digit
char::is_whitespace
<[[T; N]]>::as_flattened
<[[T; N]]>::as_flattened_mut
String::into_bytes
String::as_str
String::capacity
String::as_bytes
String::len
String::is_empty
String::as_mut_str
String::as_mut_vec
Vec::as_ptr
Vec::as_slice
Vec::capacity
Vec::len
Vec::is_empty
Vec::as_mut_slice
Vec::as_mut_ptr
Удаление таргета 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)
ptr128
17.05.2025 16:00Я уже теряю надежду увидеть в Rust стабильный ABI, вместо костыля abi_stable с extern "C".
Я понимаю, что консольные утилиты можно собирать и со статическим связыванием. Но что-то более-менее серьёзное без динамического связывания нежизнеспособно.
Уже молчу о стандарте языка, который всё более востребован в связи все с более частой необходимостью использовать GCC или форки rustc для компиляции под RISC-V или иные платформы, для которых LLVM по ряду причин не имеет стабильного кодогенератора.
AbitLogic
17.05.2025 16:00А можно поподробнее чего там llvm не имеет, мы применяем для ARM, пока никаких проблем не встречали, чего там не так с risc-v? А то может пора следить за gccrs, а мужики то и не знают
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 forRISC-V
targetsLLVM
fork with support forXtensa
targetsGCC 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 года.
AbitLogic
Обновили бы русский rust book, а то там люди до сих пор листают 1.67