nixcfg/config/agents/skills/rust-recent-features/SKILL.md
Harald Hoyer a935e77f83 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.
2026-05-21 23:34:28 +02:00

9 KiB
Raw Blame History

name description
rust-recent-features 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(...):

    #[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:

    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:

    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:

    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:
    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:
    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:
    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:
    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_arrayOption<&[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
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.