rgoussu@goussu: ~/library/rust/compilers-and-runtimes
~/library/rust/compilers-and-runtimes cat editions.md

Editions — Rust's compatibility mechanism

# How editions let Rust make breaking changes without breaking the ecosystem — per-crate opt-in, guaranteed interop, cargo fix migrations, and what each edition changed.

Conceptsaved 2026-08-09 #rust#editions#compatibility#governance

Overview

Editions are Rust's answer to the question every language eventually faces: how do you fix early mistakes without a Python-3 schism? An edition is a per-crate opt-in dialect (edition = "2021" in Cargo.toml) that may change syntax, keywords and defaults — and crates on different editions link together freely, forever, because every edition compiles to the same internal representation in the same compiler. Java answers this with near-total stasis plus preview gates, Go with the Go 1 promise plus GODEBUG; editions are the third design: breaking changes made routine, scoped, and automated.

Key points

  • Per-crate, not per-program: your 2024-edition binary can depend on 2015-edition crates without anyone noticing; there is no ecosystem flag-day. This interop guarantee is the load-bearing promise — an edition may never change what a crate's interface means to other crates.
  • What editions may change: keyword additions (async/await reserved in 2018, gen in 2024), default lints becoming errors, prelude contents, closure capture rules, syntax meaning. What they may not: anything requiring coordinated upgrades — no ABI, no stdlib removals.
  • Migration is tooled: cargo fix --edition applies mechanical rewrites, then you flip the edition key; the compiler's rust_2024_compatibility lint group warns ahead of time. Real projects migrate in an afternoon; that this is boring is the design's success metric.
  • The train: editions ship roughly every three years — 2015, 2018, 2021, 2024 (stabilized in Rust 1.85) — deliberately small; features land continuously in ordinary releases, and an edition only bundles the changes that need an opt-in.
  • 2018: async/await keywords, uniform paths, NLL rollout era, dyn Trait syntax; the module-system cleanup that removed extern crate.
  • 2021: disjoint closure captures (closures capture fields, not whole structs), IntoIterator for arrays, the new prelude (TryInto et al.).
  • 2024: RPIT lifetime-capture rules, unsafe_op_in_unsafe_fn by default, references to static mut disallowed, gen reserved — the largest edition yet, still an afternoon's migration.
  • The limit case: editions can't fix everything — anything touching the stdlib's actual types (an Error redesign, say) or coordinated semantics stays impossible; that pressure is what keeps "Rust 2.0" discussions alive and always rejected.

Details

The mechanics

The compiler front-end parses each crate according to its declared edition, then everything meets in the same HIR/MIR. A keyword in one edition is a plain identifier in another; a lint that is deny-by-default in 2024 is warn in 2021. Because the semantics of the compiled output are shared, the interop guarantee holds by construction rather than by discipline — the design insight the C++ epochs proposal tried and failed to import, largely because C++ lacks the single-implementation, single-build-system substrate Rust's editions lean on (cargo knows every crate's edition; C++ builds don't even agree on a manifest).

Contrast with the neighbours

Java Go Rust
Promise Binary + source compat, decades Go 1 promise since 2012 Interop across editions, forever
Breaking mechanism Almost never; deprecate + wait GODEBUG-gated behaviour keyed to go.mod version Edition opt-in per crate
Migration tooling go fix (rarely needed) cargo fix --edition
Cadence Preview → final per feature Per release Edition every ~3 years

Go's go directive in go.mod has converged on something edition-shaped (behaviour keyed to a declared language version); editions remain the more general instrument — they can change syntax, not just behaviour defaults.

Related

Citations

[1] The Edition Guide [2] Rust 2024 edition announcement (Rust 1.85)