feat(skills): add rust-recent-features reference skill

Documents Rust language, stdlib, and Cargo features stabilized after the
~2025 training cutoff (1.85–1.95, 2024 edition) so agents generate
current syntax instead of relying on a stale mental model.
This commit is contained in:
Harald Hoyer 2026-05-21 23:34:28 +02:00
parent 9986d286b1
commit a935e77f83

View file

@ -0,0 +1,207 @@
---
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<Item = &str> + use<'_> { /* ... */ }
fn no_capture<T>() -> 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<F: AsyncFn(u32) -> 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::<N>()` / `as_mut_array``Option<&[T; N]>` (1.93). Prefer
this over `try_into()` gymnastics for fixed-size views.
- `<[T]>::array_windows::<N>()` (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`.
- `<iN>::unchecked_neg/shl/shr`, `<uN>::unchecked_shl/shr` (1.93).
- `NonZero<u{N}>::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<T>;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_<crate>` 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::<N>()` |
| 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.