Все детали и изменения, которые произошли с Rust 1.82.0

Все детали и изменения, которые произошли с Rust 1.82.0

27 июля 2025 г.

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

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

$ rustup update stable

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

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

Что в 1,82,0 стабильно

cargo info

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

Например, вот что вы могли бы увидетьcargo info cc:

cc #build-dependencies
A build-time dependency for Cargo build scripts to assist in invoking the native
C compiler to compile native C code into a static archive to be linked into Rust
code.
version: 1.1.23 (latest 1.1.30)
license: MIT OR Apache-2.0
rust-version: 1.63
documentation: https://docs.rs/cc
homepage: https://github.com/rust-lang/cc-rs
repository: https://github.com/rust-lang/cc-rs
crates.io: https://crates.io/crates/cc/1.1.23
features:
  jobserver = []
  parallel  = [dep:libc, dep:jobserver]
note: to see how you depend on cc, run `cargo tree --invert --package cc@1.1.23`

По умолчанию,cargo infoописывает версию пакета в локальномCargo.lock, если есть. Как видите, это будет указывать, когда есть и более новая версия, иcargo info cc@1.1.30сообщит об этом.

Apple Target Promotions

macOS на 64-битной руке теперь уровня 1

Цель ржавчиныaarch64-apple-darwinДля MacOS на 64-битной руке (M1-семейство или более позднее яблочное кремниевое ЦП) теперь является целью 1-го уровня, что указывает на нашу самую высокую гарантию правильной работы. КакПоддержка платформыПейдж описывает, что каждое изменение в репозитории Rust должно проходить полные тесты на каждой цели уровня 1, прежде чем его можно будет объединить.

Эта цель была представлена как уровень 2 в Rust 1.49, что делает ее доступным вrustupПолем Этот новый веха ставитaarch64-apple-darwinЦель наравне с 64-битным ARM Linux и x86 MacOS, Linux и Windows.

Mac Catalyst Targets теперь уровня 2

MAC Catalystэто технология Apple, которая позволяет использовать приложения для iOS назначать на Mac. Это особенно полезно при тестировании специфичного для iOS, какcargo test --target=aarch64-apple-ios-macabi --target=x86_64-apple-ios-macabiВ основном просто работает (в отличие от обычных целей iOS, которые должны быть связаны с использованием внешнего инструмента, прежде чем их можно будет работать на собственном устройстве или в симуляторе).

Целитеперь уровень 2 и можно скачать сrustup target add aarch64-apple-ios-macabi x86_64-apple-ios-macabiТак теперь отличное время для обновления вашего CI Pipeline, чтобы проверить, что ваш код также работает в средах, подобных iOS.

Точный захватuse<..>синтаксис

Ржавчина теперь поддерживаетuse<..>синтаксис внутри определенногоimpl Traitграницы для управления, какие общие параметры срока службы захвачены.

Поставка возвратаimpl Trait(RPIT) Типы в ржавчинезахватыватьОпределенные общие параметры. Захват общего параметра позволяет использовать этот параметр в скрытом типе. Это, в свою очередь, влияет на проверку заимствований.

В Rust 2021 и в более ранних изданиях параметры жизни не фиксируются в непрозрачных типах на обнаженных функциях, а также на функциях и методах присущих имплс, если эти параметры времени жизни не упомянуты синтаксически в непрозрачном типе. Например, это ошибка:

//@ edition: 2021
fn f(x: &()) -> impl Sized { x }


error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
 --> src/main.rs:1:30
  |
1 | fn f(x: &()) -> impl Sized { x }
  |         ---     ----------   ^
  |         |       |
  |         |       opaque type defined here
  |         hidden type `&()` captures the anonymous lifetime defined here
  |
help: add a `use<...>` bound to explicitly capture `'_`
  |
1 | fn f(x: &()) -> impl Sized + use<'_> { x }
  |                            +++++++++

С новымuse<..>Синтаксис, мы можем исправить это, как предложено в ошибке, написав:

fn f(x: &()) -> impl Sized + use<'_> { x }

Ранее правильно исправить этот класс ошибки, требующий определения фиктивной черты, условно вызываемойCapturesи используя его следующим образом:

trait Captures<T: ?Sized> {}
impl<T: ?Sized, U: ?Sized> Captures<T> for U {}

fn f(x: &()) -> impl Sized + Captures<&'_ ()> { x }

Это было вызвано"CapturesТрюк », и это было немного барокко и тонко. Это больше не нужно.

Был менее правильный, но более удобный способ исправить это, которое часто использовалось"Уловка переживок"Полем Компилятор даже ранее предложил сделать это. Этот трюк выглядел так:

fn f(x: &()) -> impl Sized + '_ { x }

В этом простом случае хитрость точно эквивалентно+ use<'_>По тонким причинам, объясненным вRFC 3498Полем Тем не менее, в реальных случаях это превышает конструкцию границ возвращаемого непрозрачного типа, что приводит к проблемам. Например, рассмотрим этот код, который вдохновлен реальным случаем в компиляторе ржавчины:

struct Ctx<'cx>(&'cx u8);

fn f<'cx, 'a>(
    cx: Ctx<'cx>,
    x: &'a u8,
) -> impl Iterator<Item = &'a u8> + 'cx {
    core::iter::once_with(move || {
        eprintln!("LOG: {}", cx.0);
        x
    })
//~^ ERROR lifetime may not live long enough
}

Мы не можем удалить+ 'cx, поскольку срок службы используется в скрытом типе и поэтому должен быть захвачен. Мы также не можем добавить границу'a: 'cx, поскольку эти жизни на самом деле не связаны, и в целом не будет правдой, что'aпереливы'cxПолем Если мы напишем+ use<'cx, 'a>Вместо этого, однако, это будет работать и иметь правильные границы.

Есть некоторые ограничения на то, что мы стабилизируем сегодня. Аuse<..>Синтаксис в настоящее время не может появиться в пределах признаков или в пределах признаков (но обратите внимание, что там, параметры времени жизни в сфере срока действия уже захватываются по умолчанию), и он должен перечислить все общие типы и параметры в рамках. Мы надеемся поднять эти ограничения с течением времени.

Обратите внимание, что в Rust 2024 приведенные выше примеры будут «просто работать» без необходимостиuse<..>Синтаксис (или любые уловки). Это связано с тем, что в новом издании непрозрачные типы будут автоматически захватывать все параметры времени жизни в области. Это лучший дефолт, и мы увидели много доказательств того, как это очищает код. В Rust 2024,use<..>Синтаксис будет служить важным способом отказа от этого дефолта.

Для получения более подробной информации оuse<..>синтаксис, захват и как это относится к Rust 2024, см."Правила захвата RPIT Lifetime"Глава руководства издания. Для получения подробной информации об общем направлении, см. Наше недавнее сообщение в блоге,"Изменения вimpl Traitв Rust 2024 ".

Нативный синтаксис для создания необработанного указателя

Небезопасенный код иногда должен иметь дело с указателями, которые могут дать, может быть смещен или не может указывать на действительные данные. Общий случай, когда это возникаетrepr(packed)структуры. В таком случае важно избегать создания ссылки, так как это вызвало бы неопределенное поведение. Это означает обычное&и&mutОператоры не могут быть использованы, так как те, которые создают ссылку - даже если ссылка немедленно приводит к необработанному указателю, уже слишком поздно, чтобы избежать неопределенного поведения.

В течение нескольких лет макросыstd::ptr::addr_of!иstd::ptr::addr_of_mut!служили этой цели. Теперь пришло время обеспечить правильный собственный синтаксис для этой операции:addr_of!(expr)становится&raw const expr, иaddr_of_mut!(expr)становится&raw mut exprПолем Например:

#[repr(packed)]
struct Packed {
    not_aligned_field: i32,
}

fn main() {
    let p = Packed { not_aligned_field: 1_82 };

    // This would be undefined behavior!
    // It is rejected by the compiler.
    //let ptr = &p.not_aligned_field as *const i32;

    // This is the old way of creating a pointer.
    let ptr = std::ptr::addr_of!(p.not_aligned_field);

    // This is the new way.
    let ptr = &raw const p.not_aligned_field;

    // Accessing the pointer has not changed.
    // Note that `val = *ptr` would be undefined behavior because
    // the pointer is not aligned!
    let val = unsafe { ptr.read_unaligned() };
}

Нативный синтаксис дает более ясно, что экспрессия операнда этих операторов интерпретируется какРасположение выраженияПолем Это также избегает термина «адресат» при ссылке на действие создания указателя. Указатель естьБольше, чем просто адресТаким образом, ржавчина уходит от таких терминов, как «адрес», которые подтверждают ложную эквивалентность указателей и адресов.

Безопасные предметы сunsafe extern

Код ржавчины может использовать функции и статику из иностранного кода. Типовые подписи этих иностранных предметов представлены вexternблоки. Исторически все предметы внутриexternБлоки были небезопасными для использования, но нам не нужно было писатьunsafeгде угодноexternБлок сам.

Однако, если подпись вexternБлок неверен, затем использование этого элемента приведет к неопределенному поведению. Это была бы вина человека, который написалexternБлок или человек, который использовал этот предмет?

Мы решили, что это обязанность человека, пишущегоexternБлок, чтобы гарантировать, что все подписи, содержащиеся в нем, являются правильными, и поэтому мы теперь разрешаем писатьunsafe extern:

unsafe extern {
    pub safe static TAU: f64;
    pub safe fn sqrt(x: f64) -> f64;
    pub unsafe fn strlen(p: *const u8) -> usize;
}

Одним из преимуществ этого является то, что элементы вunsafe externБлок может быть помечен как безопасное использование. В приведенном выше примере мы можем позвонитьsqrtили читатьTAUбез использованияunsafeПолем Предметы, которые не отмечены нимиsafeилиunsafeконсервативно предполагаетсяunsafeПолем

В будущих выпусках мы будем поощрять использованиеunsafe externс линтами. Начиная с Rust 2024, используяunsafe externпотребуется.

Для получения дополнительной информации см.RFC 3484и"Небезопасные внешние блоки"Глава руководства издания.

Небезопасные атрибуты

Некоторые атрибуты ржавчины, такие какno_mangle, может быть использован длявызвать неопределенное поведение без какого -либоunsafeблокировать. Если бы это был обычный код, мы бы потребовали от их размещения вunsafe {}Блок, но пока атрибуты не имели сопоставимого синтаксиса. Чтобы отразить тот факт, что эти атрибуты могут подорвать гарантии безопасности Руста, они теперь считаются «небезопасными» и должны быть написаны следующим образом:

#[unsafe(no_mangle)]
pub fn my_global_function() { }

Старая форма атрибута (безunsafe) в настоящее время все еще принимается, но может быть снят в какой -то момент в будущем, и будет жесткой ошибкой в Rust 2024.

Это влияет на следующие атрибуты:

  • no_mangle
  • link_section
  • export_name

Для получения дополнительной информации см."Небезопасные атрибуты"Глава руководства издания.

Опустив пустые типы в сопоставлении рисунков

Шаблоны, которые соответствуют пустым (a.k.a. необитаемые) типы по значению теперь могут быть опущены:

use std::convert::Infallible;
pub fn unwrap_without_panic<T>(x: Result<T, Infallible>) -> T {
    let Ok(x) = x; // the `Err` case does not need to appear
    x
}

Это работает с пустыми типами, такими как без вариантаenum Void {}, или препараты и перечисления с видимым пустым поле#[non_exhaustive]атрибут. Это также будет особенно полезно в сочетании с типом никогда!, хотя этот тип все еще нестабилен в это время.

Есть некоторые случаи, когда пустые шаблоны все еще должны быть написаны. По причинам, связанным с неонициализированными значениями и небезопасным кодом, пропущение шаблонов не допускается, если пустой тип доступен через референцию, указатель или полю союза:

pub fn unwrap_ref_without_panic<T>(x: &Result<T, Infallible>) -> &T {
    match x {
        Ok(x) => x,
        // this arm cannot be omitted because of the reference
        Err(infallible) => match *infallible {},
    }
}

Чтобы не вмешиваться в ящики, которые хотят поддержать несколько версий ржавчины,matchОружие с пустыми моделями еще не сообщается как предупреждения «недоступного кода», несмотря на то, что их можно удалить.

Семантика NAN с плавающей точкой иconst

Операции на значениях с плавающей точкой (типаf32иf64), классно тонкие. Одной из причин этого является существование значений NAN («не число»), которые используются для представления, например, результат0.0 / 0.0Полем Что делает значения NAN тонкими, так это то, что существует более чем одно из возможных значений NAN. Значение NAN имеет знак (который можно проверить сf.is_sign_positive()) и полезная нагрузка (которая может быть извлечена с помощьюf.to_bits())

Однако как знак, так и полезную нагрузку значений NAN полностью игнорируются==(который всегда возвращаетсяfalse) Несмотря на очень успешные усилия по стандартизации поведения операций с плавающей точкой в области аппаратных архитектур, детали того, когда NAN является положительным или отрицательным, и то, что его точная полезная нагрузка отличается в разных архитектурах.

Чтобы сделать вопросы еще более сложными, Rust и его бэкэнд LLVM применяют оптимизации к операциям с плавающей запятой, когда точный числовой результат гарантированно не изменяется, но эти оптимизации могут изменить, что создается значение NAN. Например,f * 1.0может быть оптимизирован на простоfПолем Однако, еслиfэто Нэн, это может изменить точный бит -шаблон результата!

С помощью этого выпуска Rust стандартизирует набор правил, как ведут себя значения NAN. Этот набор правилнетполностью детерминированный, что означает, что результат таких операций, как(0.0 / 0.0).is_sign_positive()может отличаться в зависимости от аппаратной архитектуры, уровней оптимизации и окружающего кода. Код, который направлен на то, чтобы быть полностью переносимым, должен избежать использованияto_bitsи должен использоватьf.signum() == 1.0вместоf.is_sign_positive()Полем

Тем не менее, правила тщательно выбираются, чтобы все еще разрешать расширенные методы представления данных, такие как NAN Boxing, быть реализованным в коде Rust. Для получения более подробной информации о том, каковы точные правила, проверьте нашидокументацияПолем

С учетом того, что семантика для значений NAN этот выпуск также позволяет использовать операции с плавающей точкой вconst fnПолем По причинам, описанным выше, такие операции, как(0.0 / 0.0).is_sign_positive()(который будет постоянным в Rust 1,83) может привести к другому результату при выполнении во время компиляции по сравнению с временем выполнения. Это не ошибка, и код не должен полагаться наconst fnВсегда производит тот же результат.

Константы как сразу же

АconstАссамблея операнда теперь предоставляет способ использовать целые числа в качестве немедленных, не храня их в регистре. В качестве примера мы реализуем Syscall дляwriteвручную:

const WRITE_SYSCALL: c_int = 0x01; // syscall 1 is `write`
const STDOUT_HANDLE: c_int = 0x01; // `stdout` has file handle 1
const MSG: &str = "Hello, world!\n";

let written: usize;

// Signature: `ssize_t write(int fd, const void buf[], size_t count)`
unsafe {
    core::arch::asm!(
        "mov rax, {SYSCALL} // rax holds the syscall number",
        "mov rdi, {OUTPUT}  // rdi is `fd` (first argument)",
        "mov rdx, {LEN}     // rdx is `count` (third argument)",
        "syscall            // invoke the syscall",
        "mov {written}, rax // save the return value",
        SYSCALL = const WRITE_SYSCALL,
        OUTPUT = const STDOUT_HANDLE,
        LEN = const MSG.len(),
        in("rsi") MSG.as_ptr(), // rsi is `buf *` (second argument)
        written = out(reg) written,
    );
}

assert_eq!(written, MSG.len());

Выход:

Hello, world!

Ссылка на игровой площадкеПолем

В вышеперечисленном утверждение, такое какLEN = const MSG.len()заполняет спецификатор форматаLENс непосредственным, который берет ценностьMSG.len()Полем Это можно увидеть в сгенерированной сборке (значение14):

lea     rsi, [rip + .L__unnamed_3]
mov     rax, 1    # rax holds the syscall number
mov     rdi, 1    # rdi is `fd` (first argument)
mov     rdx, 14   # rdx is `count` (third argument)
syscall # invoke the syscall
mov     rax, rax  # save the return value

ВидетьСсылкаДля получения более подробной информации.

Безопасно обращаться к небезопасностиstaticс

Этот код теперь разрешен:

static mut STATIC_MUT: Type = Type::new();
extern "C" {
    static EXTERN_STATIC: Type;
}
fn main() {
     let static_mut_ptr = &raw mut STATIC_MUT;
     let extern_static_ptr = &raw const EXTERN_STATIC;
}

В контексте выражения,STATIC_MUTиEXTERN_STATICявляютсяПоместите выраженияПолем Раньше проверяющие безопасность компилятора не знали, что оператор необработанного рефлекса на самом деле не влиял на место операнда, рассматривая его как возможное чтение или запись указателю. Однако на самом деле нет недоступности, так как это просто создает указатель.

Расслабление это может вызвать проблемы, когда некоторые небезопасные блоки теперь сообщаются как неиспользованные, если вы отрицаетеunused_unsafeЛинт, но теперь они полезны только для старых версий. Аннотировать эти небезопасные блоки#[allow(unused_unsafe)]Если вы хотите поддержать несколько версий ржавчины, как в этом примере Diff:

 static mut STATIC_MUT: Type = Type::new();
 fn main() {
+    #[allow(unused_unsafe)]
     let static_mut_ptr = unsafe { std::ptr::addr_of_mut!(STATIC_MUT) };
 }

Ожидается, что будущая версия Rust будет обобщать это на другие выражения, которые будут безопасны в этой позиции, а не только статике.

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

  • std::thread::Builder::spawn_unchecked
  • std::str::CharIndices::offset
  • std::option::Option::is_none_or
  • [T]::is_sorted
  • [T]::is_sorted_by
  • [T]::is_sorted_by_key
  • Iterator::is_sorted
  • Iterator::is_sorted_by
  • Iterator::is_sorted_by_key
  • std::future::Ready::into_inner
  • std::iter::repeat_n
  • impl<T: Clone> DoubleEndedIterator for Take<Repeat<T>>
  • impl<T: Clone> ExactSizeIterator for Take<Repeat<T>>
  • impl<T: Clone> ExactSizeIterator for Take<RepeatWith<T>>
  • impl Default for std::collections::binary_heap::Iter
  • impl Default for std::collections::btree_map::RangeMut
  • impl Default for std::collections::btree_map::ValuesMut
  • impl Default for std::collections::vec_deque::Iter
  • impl Default for std::collections::vec_deque::IterMut
  • Rc<T>::new_uninit
  • Rc<MaybeUninit<T>>::assume_init
  • Rc<[T]>::new_uninit_slice
  • Rc<[MaybeUninit<T>]>::assume_init
  • Arc<T>::new_uninit
  • Arc<MaybeUninit<T>>::assume_init
  • Arc<[T]>::new_uninit_slice
  • Arc<[MaybeUninit<T>]>::assume_init
  • Box<T>::new_uninit
  • Box<MaybeUninit<T>>::assume_init
  • Box<[T]>::new_uninit_slice
  • Box<[MaybeUninit<T>]>::assume_init
  • core::arch::x86_64::_bextri_u64
  • core::arch::x86_64::_bextri_u32
  • core::arch::x86::_mm_broadcastsi128_si256
  • core::arch::x86::_mm256_stream_load_si256
  • core::arch::x86::_tzcnt_u16
  • core::arch::x86::_mm_extracti_si64
  • core::arch::x86::_mm_inserti_si64
  • core::arch::x86::_mm_storeu_si16
  • core::arch::x86::_mm_storeu_si32
  • core::arch::x86::_mm_storeu_si64
  • core::arch::x86::_mm_loadu_si16
  • core::arch::x86::_mm_loadu_si32
  • core::arch::wasm32::u8x16_relaxed_swizzle
  • core::arch::wasm32::i8x16_relaxed_swizzle
  • core::arch::wasm32::i32x4_relaxed_trunc_f32x4
  • core::arch::wasm32::u32x4_relaxed_trunc_f32x4
  • core::arch::wasm32::i32x4_relaxed_trunc_f64x2_zero
  • core::arch::wasm32::u32x4_relaxed_trunc_f64x2_zero
  • core::arch::wasm32::f32x4_relaxed_madd
  • core::arch::wasm32::f32x4_relaxed_nmadd
  • core::arch::wasm32::f64x2_relaxed_madd
  • core::arch::wasm32::f64x2_relaxed_nmadd
  • core::arch::wasm32::i8x16_relaxed_laneselect
  • core::arch::wasm32::u8x16_relaxed_laneselect
  • core::arch::wasm32::i16x8_relaxed_laneselect
  • core::arch::wasm32::u16x8_relaxed_laneselect
  • core::arch::wasm32::i32x4_relaxed_laneselect
  • core::arch::wasm32::u32x4_relaxed_laneselect
  • core::arch::wasm32::i64x2_relaxed_laneselect
  • core::arch::wasm32::u64x2_relaxed_laneselect
  • core::arch::wasm32::f32x4_relaxed_min
  • core::arch::wasm32::f32x4_relaxed_max
  • core::arch::wasm32::f64x2_relaxed_min
  • core::arch::wasm32::f64x2_relaxed_max
  • core::arch::wasm32::i16x8_relaxed_q15mulr
  • core::arch::wasm32::u16x8_relaxed_q15mulr
  • core::arch::wasm32::i16x8_relaxed_dot_i8x16_i7x16
  • core::arch::wasm32::u16x8_relaxed_dot_i8x16_i7x16
  • core::arch::wasm32::i32x4_relaxed_dot_i8x16_i7x16_add
  • core::arch::wasm32::u32x4_relaxed_dot_i8x16_i7x16_add

Эти API теперь стабильны в контексте:

  • std::task::Waker::from_raw
  • std::task::Context::from_waker
  • std::task::Context::waker
  • $integer::from_str_radix
  • std::num::ParseIntError::kind

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

Проверьте все, что изменилось вРжавчинаВГруз, иКлиппиПолем

Авторы 1,82,0

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


Команда релиза ржавчины

Также опубликованоздесь

ФотоМилад ФакурианнаНеспособный


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE