What’s New & Edition Migration

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Rust 1.99.0 (stable since 2026-10-01), checked on 2026-10-06; Rust 1.100 is due on 2026-11-12. It collects in one place what has changed since the material most tutorials and books are based on: the 2024 edition, each stable release from 1.75 onwards, and, at the end, every place where the books consulted for this section describe an older form. Each change is explained in depth on the page linked from it.

The 2024 edition

The 2024 edition was stabilised in Rust 1.85 (2025-02-20). New Cargo projects use it by default. Its changes, grouped as in the Edition Guide:

Language

Change What it means Page

RPIT lifetime capture

impl Trait in return position captures all in-scope lifetimes; use use<..> to capture less

Lifetimes

if let temporary scope

temporaries in the if let condition are dropped before the else block (fixes lock-in-else deadlocks)

Control Flow

Tail-expression temporary scope

temporaries in a block’s tail expression are dropped before its locals

Control Flow

Match ergonomics reservations

explicit ref/mut/& inside patterns that already bind by reference are errors

Borrowing & References

Unsafe extern blocks

unsafe extern "C" { ... }, with safe/unsafe items

FFI & C Interop

Unsafe attributes

#[unsafe(no_mangle)], #[unsafe(export_name)], #[unsafe(link_section)]

Unsafe Rust

unsafe_op_in_unsafe_fn warns

unsafe operations inside unsafe fn need their own unsafe {} block

Unsafe Rust

References to static mut denied

static_mut_refs is deny-by-default; use atomics, locks or &raw

Variables, Mutability & Constants

Never-type fallback

unconstrained ! falls back to ! instead of (), which can change inference in rare unsafe code

 — 

Macro fragment specifiers

expr also matches const {} and _; expr_2021 keeps the old meaning; a missing fragment specifier is an error

Macros

gen keyword

reserved for future generator blocks

Iterators

Reserved syntax

#"..."# guarded strings and ## are reserved for future use

 — 

// 2024: let chains (1.88), unsafe attribute and the if-let temporary scope in one place.
use std::sync::Mutex;

#[unsafe(no_mangle)] // 2021: #[no_mangle]
pub extern "C" fn shelf_version() -> u32 {
    3
}

fn take(stock: &Mutex<Vec<&'static str>>) -> &'static str {
    if let Some(t) = stock.lock().unwrap().pop()
        && !t.is_empty()
    {
        t
    } else {
        stock.lock().unwrap().push("Dune"); // 2021: deadlock, the first guard was still alive here
        "restocked"
    }
}

fn main() {
    let stock = Mutex::new(vec![]);
    println!("{} {} v{}", take(&stock), take(&stock), shelf_version());
}

Standard library

  • Future and IntoFuture are in the prelude.

  • Box<[T]> implements IntoIterator by value (boxed_slice.into_iter() yields T).

  • std::env::set_var, std::env::remove_var and CommandExt::before_exec are unsafe (see I/O, Files, Processes & Environment).

Cargo

  • The MSRV-aware resolver (resolver = "3") is the default for 2024 packages.

  • Manifests reject the old underscore key spellings (dev_dependencies, build_dependencies, crate_types, default_features, proc_macro).

  • An unused default-features = false on an inherited workspace dependency is an error.

rustdoc and rustfmt

  • Combined doctests: rustdoc compiles all doctests of a crate into one binary where possible, which is much faster; mark a test standalone_crate to opt out. Nested include! in doctests resolves paths relative to the including file.

  • rustfmt has a style edition independent of the language edition (style_edition = "2024"): version sorting of identifiers, formatting fixes, and a different sort order for raw identifiers.

Migrating a crate with cargo fix --edition

rustup update stable
cargo update && cargo test                 # start green on the old edition
cargo fix --edition --all-targets --all-features   # apply the migration lints while still on 2021
# edit Cargo.toml: edition = "2024" (and in [workspace.package] for workspaces)
cargo build --all-targets && cargo test    # fix whatever the lints could not
cargo fmt                                  # reformat with the 2024 style edition
cargo clippy --all-targets -- -D warnings

cargo fix --edition is conservative: it may add use<..> bounds, rewrite if let into match to preserve old drop order, change expr to expr_2021 in macros and wrap set_var calls in unsafe blocks. Review those rewrites; most can be simplified once you have checked that the new behaviour is what you want. Migrate one crate of a workspace at a time — crates of different editions interoperate freely.

Release by release: 1.75 to 1.99

Rust ships every six weeks. The table lists headline features per release; each was compiled against that release’s toolchain while writing this section where it is used in an example.

Release Date Headline features

1.75

2023-12

async fn and return-position impl Trait in traits

1.76

2024-02

type_name_of_val; Option::inspect, Result::inspect/inspect_err

1.77

2024-03

C-string literals c"..."; recursion in async fn with Box::pin; offset_of!; cargo:: build-script syntax

1.78

2024-05

#[diagnostic::on_unimplemented]; wasm32-wasi renamed wasm32-wasip1

1.79

2024-06

inline const {} blocks; generic NonZero<T>; temporary lifetime extension through if/match; bounds in associated type position

1.80

2024-07

LazyCell/LazyLock; exclusive range patterns a..b; checked cfg names (unexpected_cfgs); size_of in the prelude

1.81

2024-09

#[expect] and lint reasons; core::error::Error; new sort implementations that detect inconsistent Ord; PanicHookInfo

1.82

2024-10

precise capturing use<..>; &raw const/&raw mut; unsafe extern blocks and unsafe attributes (syntax); cargo info; is_none_or; iter::repeat_n; const float arithmetic

1.83

2024-11

&mut and interior mutability in const; many more `const fn`s

1.84

2025-01

MSRV-aware resolver; strict provenance APIs; isqrt; wasm32-wasi target removed

1.85

2025-02

Rust 2024 edition; async closures and AsyncFn*; #[diagnostic::do_not_recommend]; FromIterator/ Extend for larger tuples

1.86

2025-04

trait upcasting; get_disjoint_mut; Vec::pop_if; #[target_feature] on safe functions

1.87

2025-05

io::pipe; Vec::extract_if; is_multiple_of; asm_goto

1.88

2025-06

let chains (2024 edition); naked functions; boolean literals in cfg; Cell::update; as_chunks

1.89

2025-08

inferred const arguments [T; _]; File::lock; the mismatched_lifetime_syntaxes lint

1.90

2025-09

LLD as the default linker on x86_64-unknown-linux-gnu; cargo publish --workspace; x86_64-apple-darwin moved to tier 2

1.91

2025-10

strict_* integer arithmetic; PanicHookInfo::payload_as_str

1.92

2025-12

RwLockWriteGuard::downgrade; Box/Rc/Arc::new_zeroed

1.93

2026-01

fmt::from_fn

1.94

2026-03

slice::array_windows; include in .cargo/config.toml

1.95

2026-04

cfg_select!; if let guards in match; Vec::push_mut; Atomic*::update/try_update; hint::cold_path; core::range::RangeInclusive

1.96

2026-05

assert_matches!; core::range::Range

1.97

2026-07

v0 symbol mangling by default

1.98

2026-08

bool::ok_or/ok_or_else; algebraic float methods; str::strip_circumfix

1.99

2026-10

C-variadic function definitions; String::from_utf8_lossy_owned; size_of_val_raw; fs::set_times; attribute macros on non-inline modules

The Rust blog (https://blog.rust-lang.org/) announces each release with examples, and https://releases.rs lists every stabilised API and change per version, including the upcoming beta.

Work in progress (nightly only)

Several long-running efforts are visible on nightly but not part of stable Rust:

  • Polonius, a more precise borrow checker that accepts some programs NLL rejects (notably conditional returns of borrows), being integrated as a location-sensitive analysis;

  • the next-generation trait solver, already used for coherence checking, being extended to all trait solving;

  • generators and gen blocks (the keyword is reserved), specialisation, const generic expressions (generic_const_exprs), the ! type in all positions, and async drop;

  • Cranelift as a faster code-generation backend for debug builds.

A 2027 edition has been discussed by the project but is not announced; treat any list of its contents as speculative. The project’s goals for each half-year are published at https://goals.rust-lang.org/.

Outdated in the books

The books consulted for this section (listed in the section bibliography) were used for concept coverage; where they show an older form, the pages present the current one. The complete list:

Area In the books Current

Editions

Mara & Holmes use 2018; Yoon and Crossley et al. use 2021

2024 (1.85): RPIT capture, if let and tail-expression scopes, unsafe extern, unsafe attributes, unsafe_op_in_unsafe_fn, no &static mut, gen reserved, set_var unsafe, MSRV-aware resolver

Missing language features

 — 

let chains (1.88), if let guards (1.95), async closures (1.85), trait upcasting (1.86), use<..> (1.82), &raw (1.82), #[expect] (1.81), LazyLock/LazyCell (1.80), cfg_select! (1.95), assert_matches! (1.96), C-variadic definitions (1.99)

Terminology

"object safety" (Azam ch. 7)

dyn compatibility

Downcasting

the as_any boilerplate (Azam ch. 8)

trait upcasting to dyn Any (1.86)

Drop check

#[may_dangle] (Azam ch. 9) presented without qualification

nightly-only (dropck_eyepatch)

Globals

lazy_static! for a JWT secret (Yoon ch. 4)

std::sync::LazyLock, and secrets in application state

FFI

#[no_mangle], bare extern "C" {}, libc::c_char (Mara & Holmes ch. 3-4, Crossley ch. 11)

#[unsafe(no_mangle)], unsafe extern "C" {}, core::ffi::c_char

FFI safety

a safe extern "C" fn dereferencing caller pointers, printing from the FFI layer (Mara & Holmes ch. 3)

unsafe extern "C" fn with a # Safety section; no side effects in the binding layer

PyO3

0.14 GIL-ref API: &PyModule, Python::with_gil, allow_threads (Mara & Holmes ch. 6-8)

0.29: Bound<'py, T>, Python::attach, py.detach, declarative #[pymodule] mod, free-threading support

WebAssembly

wasm32-wasi, WasmEdge forks (tokio_wasi, reqwest_wasi), wasm-pack, Yew 0.19 (Mara & Holmes ch. 9-10)

wasm32-wasip1/wasip2 and the component model; mainline crates; wasm-bindgen CLI or framework tooling (rustwasm org sunset in 2025); current Yew, Leptos, Dioxus

Build scripts

cargo: single-colon instructions; bindgen 0.56 (Mara & Holmes)

cargo:: (1.77), rustc-check-cfg; bindgen 0.73 with unsafe extern output

Benchmarks

criterion 0.3/0.4, criterion::black_box

criterion 0.8, std::hint::black_box

Random numbers

rand 0.8 thread_rng()/gen_range (Crossley et al., the Book’s guessing game)

rand 0.10: rand::rng(), random_range, RngExt

Numeric crates

ndarray 0.15, rayon 1.7 (Crossley et al.)

ndarray 0.17, rayon 1.12

GUI

Druid (Crossley et al.)

discontinued; its successor project is Xilem

Memory leaks

"Rust catches memory leaks at compile time" (Yoon ch. 1)

leaks are memory-safe; Rc cycles and mem::forget leak

Async state

tokio::sync::Mutex as the default (Gangappa & Rangaraj ch. 2)

std::sync::Mutex unless the guard is held across .await (tokio documentation)

Errata

"Rust 1.8 or later" for 1.80 (Gangappa & Rangaraj preface); unterminated strings in Cargo.toml snippets (Crossley et al.)

 — 

The web-framework items (axum 0.6/0.7 APIs, tower-http 0.6, sqlx 0.8, SeaORM 1, jsonwebtoken 9, bcrypt in handlers) are collected on Axum: Migration & Framework Comparison.