All the Details and Changes That Came With Rust 1.82.0

Wait 5 sec.

The Rust team is happy to announce a new version of Rust, 1.82.0. Rust is a programming language empowering everyone to build reliable and efficient software.\If you have a previous version of Rust installed via rustup, you can get 1.82.0 with:$ rustup update stableIf you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.82.0.\If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!What's in 1.82.0 stablecargo infoCargo now has an info subcommand to display information about a package in the registry, fulfilling a long-standing request just shy of its tenth anniversary! Several third-party extensions like this have been written over the years, and this implementation was developed as cargo-information before merging into Cargo itself.\For example, here's what you could see for cargo info cc:cc #build-dependenciesA build-time dependency for Cargo build scripts to assist in invoking the nativeC compiler to compile native C code into a static archive to be linked into Rustcode.version: 1.1.23 (latest 1.1.30)license: MIT OR Apache-2.0rust-version: 1.63documentation: https://docs.rs/cchomepage: https://github.com/rust-lang/cc-rsrepository: https://github.com/rust-lang/cc-rscrates.io: https://crates.io/crates/cc/1.1.23features: jobserver = [] parallel = [dep:libc, dep:jobserver]note: to see how you depend on cc, run `cargo tree --invert --package cc@1.1.23`By default, cargo info describes the package version in the local Cargo.lock, if any. As you can see, it will indicate when there's a newer version too, and cargo info cc@1.1.30 would report on that.Apple target promotionsmacOS on 64-bit ARM is now Tier 1The Rust target aarch64-apple-darwin for macOS on 64-bit ARM (M1-family or later Apple Silicon CPUs) is now a tier 1 target, indicating our highest guarantee of working properly. As the platform support page describes, every change in the Rust repository must pass full tests on every tier 1 target before it can be merged. \This target was introduced as tier 2 back in Rust 1.49, making it available in rustup. This new milestone puts the aarch64-apple-darwin target on par with the 64-bit ARM Linux and the X86 macOS, Linux, and Windows targets.Mac Catalyst targets are now Tier 2Mac Catalyst is a technology by Apple that allows running iOS applications natively on the Mac. This is especially useful when testing iOS-specific code, as cargo test --target=aarch64-apple-ios-macabi --target=x86_64-apple-ios-macabi mostly just works (in contrast to the usual iOS targets, which need to be bundled using external tooling before they can be run on a native device or in the simulator).\The targets are now tier 2, and can be downloaded with rustup target add aarch64-apple-ios-macabi x86_64-apple-ios-macabi, so now is an excellent time to update your CI pipeline to test that your code also runs in iOS-like environments.Precise capturing use syntaxRust now supports use syntax within certain impl Trait bounds to control which generic lifetime parameters are captured.\Return-position impl Trait (RPIT) types in Rust capture certain generic parameters. Capturing a generic parameter allows that parameter to be used in the hidden type. That in turn affects borrow checking.\In Rust 2021 and earlier editions, lifetime parameters are not captured in opaque types on bare functions and on functions and methods of inherent impls unless those lifetime parameters are mentioned syntactically in the opaque type. E.g., this is an error://@ edition: 2021fn 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 }\Previously, correctly fixing this class of error required defining a dummy trait, conventionally called Captures, and using it as follows:trait Captures {}impl Captures for U {}fn f(x: &()) -> impl Sized + Captures impl Sized + '_ { x }\In this simple case, the trick is exactly equivalent to + use(&'cx u8);fn f( cx: Ctx impl Iterator T { let Ok(x) = x; // the `Err` case does not need to appear x}This works with empty types such as a variant-less enum Void {}, or structs and enums with a visible empty field and no #[non_exhaustive] attribute. It will also be particularly useful in combination with the never type !, although that type is still unstable at this time.\There are some cases where empty patterns must still be written. For reasons related to uninitialized values and unsafe code, omitting patterns is not allowed if the empty type is accessed through a reference, pointer, or union field:pub fn unwrap_ref_without_panic(x: &Result) -> &T { match x { Ok(x) => x, // this arm cannot be omitted because of the reference Err(infallible) => match *infallible {}, }}To avoid interfering with crates that wish to support several Rust versions, match arms with empty patterns are not yet reported as “unreachable code” warnings, despite the fact that they can be removed.Floating-point NaN semantics and constOperations on floating-point values (of type f32 and f64) are famously subtle. One of the reasons for this is the existence of NaN ("not a number") values which are used to represent e.g. the result of 0.0 / 0.0. What makes NaN values subtle is that more than one possible NaN value exists. A NaN value has a sign (that can be checked with f.is_sign_positive()) and a payload (that can be extracted with f.to_bits()). \However, both the sign and payload of NaN values are entirely ignored by == (which always returns false). Despite very successful efforts to standardize the behavior of floating-point operations across hardware architectures, the details of when a NaN is positive or negative and what its exact payload is differ across architectures. \To make matters even more complicated, Rust and its LLVM backend apply optimizations to floating-point operations when the exact numeric result is guaranteed not to change, but those optimizations can change which NaN value is produced. For instance, f * 1.0 may be optimized to just f. However, if f is a NaN, this can change the exact bit pattern of the result!\With this release, Rust standardizes on a set of rules for how NaN values behave. This set of rules is not fully deterministic, which means that the result of operations like (0.0 / 0.0).is_sign_positive() can differ depending on the hardware architecture, optimization levels, and the surrounding code. Code that aims to be fully portable should avoid using to_bits and should use f.signum() == 1.0 instead of f.is_sign_positive(). \However, the rules are carefully chosen to still allow advanced data representation techniques such as NaN boxing to be implemented in Rust code. For more details on what the exact rules are, check out our documentation.\With the semantics for NaN values settled, this release also permits the use of floating-point operations in const fn. Due to the reasons described above, operations like (0.0 / 0.0).is_sign_positive() (which will be const-stable in Rust 1.83) can produce a different result when executed at compile-time vs at run-time. This is not a bug, and code must not rely on a const fn always producing the exact same result.Constants as assembly immediatesThe const assembly operand now provides a way to use integers as immediates without first storing them in a register. As an example, we implement a syscall to write by hand:const WRITE_SYSCALL: c_int = 0x01; // syscall 1 is `write`const STDOUT_HANDLE: c_int = 0x01; // `stdout` has file handle 1const 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());\Output:Hello, world!Playground link.\In the above, a statement such as LEN = const MSG.len() populates the format specifier LEN with an immediate that takes the value of MSG.len(). This can be seen in the generated assembly (the value is 14):lea rsi, [rip + .L__unnamed_3]mov rax, 1 # rax holds the syscall numbermov rdi, 1 # rdi is `fd` (first argument)mov rdx, 14 # rdx is `count` (third argument)syscall # invoke the syscallmov rax, rax # save the return valueSee the reference for more details.Safely addressing unsafe staticsThis code is now allowed: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;}In an expression context, STATIC_MUT and EXTERN_STATIC are place expressions. Previously, the compiler's safety checks were not aware that the raw ref operator did not actually affect the operand's place, treating it as a possible read or write to a pointer. No unsafety is actually present, however, as it just creates a pointer.\Relaxing this may cause problems where some unsafe blocks are now reported as unused if you deny the unused_unsafe lint, but they are now only useful on older versions. Annotate these unsafe blocks with #[allow(unused_unsafe)] if you wish to support multiple versions of Rust, as in this example 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) }; }\A future version of Rust is expected to generalize this to other expressions which would be safe in this position, not just statics.Stabilized APIsstd::thread::Builder::spawn_uncheckedstd::str::CharIndices::offsetstd::option::Option::is_none_or[T]::is_sorted[T]::is_sorted_by[T]::is_sorted_by_keyIterator::is_sortedIterator::is_sorted_byIterator::is_sorted_by_keystd::future::Ready::into_innerstd::iter::repeat_nimpl DoubleEndedIterator for Takeimpl ExactSizeIterator for Takeimpl ExactSizeIterator for Takeimpl Default for std::collections::binary_heap::Iterimpl Default for std::collections::btree_map::RangeMutimpl Default for std::collections::btree_map::ValuesMutimpl Default for std::collections::vec_deque::Iterimpl Default for std::collections::vec_deque::IterMutRc::new_uninitRc::assume_initRc::new_uninit_sliceRc::assume_initArc::new_uninitArc::assume_initArc::new_uninit_sliceArc::assume_initBox::new_uninitBox::assume_initBox::new_uninit_sliceBox::assume_initcore::arch::x86_64::_bextri_u64core::arch::x86_64::_bextri_u32core::arch::x86::_mm_broadcastsi128_si256core::arch::x86::_mm256_stream_load_si256core::arch::x86::_tzcnt_u16core::arch::x86::_mm_extracti_si64core::arch::x86::_mm_inserti_si64core::arch::x86::_mm_storeu_si16core::arch::x86::_mm_storeu_si32core::arch::x86::_mm_storeu_si64core::arch::x86::_mm_loadu_si16core::arch::x86::_mm_loadu_si32core::arch::wasm32::u8x16_relaxed_swizzlecore::arch::wasm32::i8x16_relaxed_swizzlecore::arch::wasm32::i32x4_relaxed_trunc_f32x4core::arch::wasm32::u32x4_relaxed_trunc_f32x4core::arch::wasm32::i32x4_relaxed_trunc_f64x2_zerocore::arch::wasm32::u32x4_relaxed_trunc_f64x2_zerocore::arch::wasm32::f32x4_relaxed_maddcore::arch::wasm32::f32x4_relaxed_nmaddcore::arch::wasm32::f64x2_relaxed_maddcore::arch::wasm32::f64x2_relaxed_nmaddcore::arch::wasm32::i8x16_relaxed_laneselectcore::arch::wasm32::u8x16_relaxed_laneselectcore::arch::wasm32::i16x8_relaxed_laneselectcore::arch::wasm32::u16x8_relaxed_laneselectcore::arch::wasm32::i32x4_relaxed_laneselectcore::arch::wasm32::u32x4_relaxed_laneselectcore::arch::wasm32::i64x2_relaxed_laneselectcore::arch::wasm32::u64x2_relaxed_laneselectcore::arch::wasm32::f32x4_relaxed_mincore::arch::wasm32::f32x4_relaxed_maxcore::arch::wasm32::f64x2_relaxed_mincore::arch::wasm32::f64x2_relaxed_maxcore::arch::wasm32::i16x8_relaxed_q15mulrcore::arch::wasm32::u16x8_relaxed_q15mulrcore::arch::wasm32::i16x8_relaxed_dot_i8x16_i7x16core::arch::wasm32::u16x8_relaxed_dot_i8x16_i7x16core::arch::wasm32::i32x4_relaxed_dot_i8x16_i7x16_addcore::arch::wasm32::u32x4_relaxed_dot_i8x16_i7x16_add\These APIs are now stable in const contexts:std::task::Waker::from_rawstd::task::Context::from_wakerstd::task::Context::waker$integer::from_str_radixstd::num::ParseIntError::kindOther changesCheck out everything that changed in Rust, Cargo, and Clippy.Contributors to 1.82.0Many people came together to create Rust 1.82.0. We couldn't have done it without all of you. Thanks!The Rust Release Team\Also published here\Photo by Milad Fakurian on Unsplash