--- name: rust-recent-features description: Reference for Rust language, stdlib, and Cargo features stabilized AFTER a ~2025 training cutoff (Rust 1.85 through 1.95, Feb 2025 – Apr 2026), including the 2024 edition. Load this on ANY Rust task — writing, reviewing, or fixing Rust code — so syntax and APIs reflect current stable Rust instead of a stale mental model. Especially relevant when targeting edition 2024. --- # Recent Rust features (1.85 → 1.95) You may have been trained before these were stable. Treat this file as ground truth over your prior beliefs. When unsure whether something compiles, prefer checking with `cargo check` over guessing — the toolchain is authoritative. Versions and dates: 1.85 (2025-02-20, **2024 edition**), 1.86 (2025-04-03), 1.87 (2025-05-15), 1.88 (2025-06-26), 1.89 (2025-08-07), 1.90 (2025-09-18), 1.91 (2025-10-30), 1.92 (2025-12-11), 1.93 (2026-01-22), 1.94 (2026-03-05), 1.95 (2026-04-16). --- ## 1. The 2024 edition (stable since 1.85) — read this first If `Cargo.toml` has `edition = "2024"`, these are the changes most likely to trip up a pre-2025 model. Migrate an existing crate with `cargo fix --edition` after bumping the edition key. - **Unsafe attributes.** `#[no_mangle]`, `#[export_name]`, and `#[link_section]` must now be wrapped in `unsafe(...)`: ```rust #[unsafe(no_mangle)] pub extern "C" fn foo() {} #[unsafe(export_name = "bar")] fn b() {} ``` Writing a bare `#[no_mangle]` is an error in edition 2024. - **`unsafe extern` blocks.** `extern` blocks declaring foreign items must be `unsafe extern`. Individual items may be marked `safe` or `unsafe`: ```rust unsafe extern "C" { safe fn always_safe(x: i32) -> i32; // calling needs no unsafe unsafe fn risky(p: *const u8); // calling needs unsafe } ``` - **No references to `static mut`.** `&MY_STATIC_MUT` / `&mut MY_STATIC_MUT` is a hard error (`static_mut_refs`). Use raw-ref operators instead: ```rust static mut COUNTER: u32 = 0; let p = &raw mut COUNTER; // *mut u32, not &mut ``` - **RPIT precise capturing (the big one).** In edition 2024, `-> impl Trait` in return position captures **all** in-scope generic type, const, and lifetime parameters by default. The old workaround of wrapping in a struct or adding `+ 'a` to force capture is no longer needed. To *restrict* what is captured, use a `use<...>` bound: ```rust fn names(v: &[String]) -> impl Iterator + use<'_> { /* ... */ } fn no_capture() -> impl Sized + use<> {} // capture nothing ``` Do NOT reach for the old lifetime-wrapper patterns; reach for `use<>`. - **`if let` temporary scope.** Temporaries in the scrutinee of an `if let … { } else { }` are dropped before the `else` branch runs (previously they lived to the end of the whole construct). This fixes a class of deadlocks where a lock guard was held across the `else`. - **Tail-expression temporary scope.** In a block, temporaries in the final tail expression are dropped *before* the block's local variables, changing `Drop` ordering in some code. - **Never-type fallback.** The `!` type now falls back to `!` (not `()`) in certain diverging contexts; this can surface new inference results or `never_type_fallback_flowing_into_unsafe` lints. - **`gen` is a reserved keyword.** Use `r#gen` if you need it as an identifier. - **Macro `expr` fragment** now also matches `const { … }` blocks and `_`. --- ## 2. New language features (by area) ### Control flow, patterns, bindings - **Let chains** (1.88, **edition 2024 only**): chain `let` with `&&` in `if`/`while`: ```rust if let Some(x) = a && let Ok(y) = parse(x) && y > 0 { /* ... */ } ``` This is stable now — do not claim it requires nightly. - **`if let` guards** (1.95): a `let` pattern in a match-arm guard: ```rust match val { v if let Some(y) = lookup(v) => use_it(y), _ => {} } ``` - **Open-start ranges after unary ops** (1.87): `..EXPR` now parses after `!`, `-`, `*`. ### Async - **Async closures** (1.85): `async || { … }` is stable, with the `AsyncFn` / `AsyncFnMut` / `AsyncFnOnce` traits. No boxing/crate needed: ```rust async fn run u32>(f: F) -> u32 { f(1).await } run(async |x| x + 1).await; ``` ### Traits & generics - **Trait upcasting** (1.86): coerce `dyn Sub` to `dyn Super` where `Sub: Super`: ```rust let up: &dyn Super = sub_ref; // now stable ``` - **`use<>` precise capturing in trait RPIT** (1.87): `use<...>` bounds allowed on return-position `impl Trait` in trait definitions. - **Inferred const args** (1.89, `generic_arg_infer`): `_` may stand in for a const generic argument, e.g. `let a: [_; _] = make();` style inference. - **`#[repr(u128)]` / `#[repr(i128)]`** enums (1.89). ### unsafe / FFI / asm - **Naked functions** (1.88): `#[unsafe(naked)]` with a `naked_asm!` body, stable. - **C-variadic *declarations*** for more ABIs: `sysv64`/`win64`/`efiapi`/`aapcs` (1.91) and `system` (1.93) `extern` fns may declare `...` variadics. - **`asm_goto`** (1.87): `asm!` may jump to label blocks. - **`asm_cfg`** (1.93): `#[cfg(...)]` allowed on individual `asm!` operands/lines. - Many target features stabilized: **AVX-512** family (1.89), AES key-locker (`kl`/`widekl`), `sha512`/`sm3`/`sm4`, `sse4a`/`tbm` (1.91), LoongArch & RISC-V profiles, etc. ### Attributes, lints, macros - **`#[diagnostic::do_not_recommend]`** (1.85): hide an impl from "consider implementing" diagnostics. - **`#[target_feature]` on safe fns** (1.86): safe functions may carry it. - **`cfg_boolean_literals`** (1.88): `#[cfg(true)]` / `#[cfg(false)]`. - **`cfg_select!`** (1.95): a `match`-like macro selecting a token tree by cfg. --- ## 3. Notable stdlib stabilizations (high-signal) Slices / arrays: - `<[T]>::as_array::()` / `as_mut_array` → `Option<&[T; N]>` (1.93). Prefer this over `try_into()` gymnastics for fixed-size views. - `<[T]>::array_windows::()` (1.94); `<[T]>::element_offset` (1.94). Vec / collections: - `Vec::push_mut`, `Vec::insert_mut` return `&mut T` to the new element (1.95); same family on `VecDeque`/`LinkedList`. - `Vec::into_raw_parts`, `String::into_raw_parts` (1.93). - `VecDeque::pop_front_if` / `pop_back_if` (1.93). - `Peekable::next_if_map` / `next_if_map_mut` (1.94). - `btree_map::Entry::insert_entry` / `VacantEntry::insert_entry` (1.92). Integers / math: - **Strict arithmetic**: `{int}::strict_add/sub/mul/div/rem/neg/shl/shr/pow` etc. (1.91) — like the `checked_*` family but panic on overflow instead of `None`. - `::unchecked_neg/shl/shr`, `::unchecked_shl/shr` (1.93). - `NonZero::div_ceil` (1.92). - `f32`/`f64` consts `EULER_GAMMA`, `GOLDEN_RATIO`; `f32/f64::mul_add` const (1.94). Sync / atomics: - `Atomic{Bool,Ptr,Isize,Usize,…}::update` / `try_update` (1.95). - `AtomicPtr::fetch_ptr_add/sub`, `fetch_byte_add/sub`, `fetch_or/and/xor` (1.91). - `RwLockWriteGuard::downgrade` to a read guard (1.92). Memory / smart pointers: - `Box/Rc/Arc::new_zeroed` and `new_zeroed_slice` (1.92). - `core::range` module with a `Copy`, cleanly-iterable `RangeInclusive` (1.95). - `MaybeUninit<[T;N]>` ⇄ `[MaybeUninit;N]` conversions; slice `assume_init_ref/mut/drop`, `write_copy_of_slice` (1.93/1.95). Misc: - `bool: TryFrom<{integer}>` (1.95); `char::MAX_LEN_UTF8/UTF16` (1.93). - `Duration::from_nanos_u128` (1.93); `Path::file_prefix` (1.91). - A large number of previously-stable APIs are now usable in `const` contexts. --- ## 4. Cargo - **2024 edition** is the default for new crates via `cargo new`. - **Automatic cache garbage collection** (1.88): the global download/source cache is pruned automatically; no manual cleanup script needed. - **`build.build-dir`** stabilized (1.91): put intermediate artifacts in a separate directory from the final `target/` outputs. - **Multi-package publishing** (1.90): publish an entire workspace in one `cargo publish` invocation, in dependency order. - `cargo clean --workspace` (1.93); `config` top-level **`include`** key (1.94) for composing config files; `CARGO_BIN_EXE_` available at runtime (1.94). - Cargo parses TOML v1.1 manifests (1.94). --- ## 5. Cheat sheet — corrections a pre-2025 model commonly needs | If you were about to write… | In current Rust (edition 2024) write… | |---|---| | `#[no_mangle]` | `#[unsafe(no_mangle)]` | | `extern "C" { … }` (declaration) | `unsafe extern "C" { … }` | | `&mut MY_STATIC_MUT` | `&raw mut MY_STATIC_MUT` | | wrapper struct / `+ 'a` to constrain RPIT | `-> impl Trait + use<'a, T>` | | "let chains need nightly" | stable in edition 2024 (since 1.88) | | box/crate an async closure | `async || { … }` is stable (1.85) | | `slice.try_into::<[T; N]>()` for a view | `slice.as_array::()` | | hand-rolled overflow-panicking add | `x.strict_add(y)` | | "trait upcasting is unstable" | stable since 1.86 | When in doubt, set the toolchain to a known version (`rustup show`) and let `cargo check` / `cargo clippy` confirm. Edition-specific behavior depends on the `edition` field in `Cargo.toml`, not the compiler version alone.