Edition Guide
Purpose and Scope
Rust editions are the project mechanism for introducing language changes that would otherwise be backward incompatible while preserving Rust's stability promise. The official Edition Guide describes editions as opt-in compatibility boundaries: a crate chooses an edition, usually through the edition key in Cargo.toml, and the compiler interprets that crate under the selected language rules. The Book appendix frames editions as a packaging point for years of incremental language and tooling improvements, rather than as a separate compiler or ecosystem. This page connects that reader-facing model to the concrete edition representations used inside the compiler and first-party tools.
The most important practical rule is that editions are per crate and interoperable. A Rust 2015 crate and a Rust 2024 crate can depend on each other because edition-specific changes are handled at language-front-end and tooling boundaries, not by creating incompatible object formats or package universes. In source, that model appears as small edition enums, comparison helpers such as at-least checks, and localized behavior switches. Those APIs let parser, macro expansion, diagnostics, formatting, tests, and editor tooling ask whether a selected edition enables a given rule without treating each edition as a wholly separate language implementation.
Sources: compiler/rustc_span/src/edition.rs, src/tools/rust-analyzer/crates/edition/src/lib.rs
Relevant Source Files
compiler/rustc_span/src/edition.rsdefines the compiler-sideEditionenum, stable and future edition constants, display names, compatibility lint names, stability checks, and feature-threshold helper methods.compiler/rustc_builtin_macros/src/edition_panic.rsshows a concrete edition-dependent language-library behavior: expansion ofpanic!andunreachable!to 2015-style or 2021-style implementations based on the call-site edition.src/tools/rust-analyzer/crates/edition/src/lib.rsdefines rust-analyzer's standalone edition support crate, including edition parsing, formatting, iteration, default, latest, and threshold helpers.src/tools/rust-analyzer/crates/edition/Cargo.tomlpackages that rust-analyzer edition logic as theeditioncrate so it can be shared by analyzer code and code generation.src/tools/compiletest/src/edition.rsdefines compiletest's edition parser, including support for numeric editions and the permanently orderedfutureedition used in tests.src/tools/rustfmt/src/config/style_edition.rsdefines rustfmt's style-edition default mechanism, including defaults that change at the 2024 style boundary and carry forward to a planned 2027 style edition.
Core Concepts
An edition name is a small, ordered value. In the compiler, compiler/rustc_span/src/edition.rs lists Edition2015, Edition2018, Edition2021, Edition2024, and EditionFuture, with a note that editions must stay ordered from oldest to newest. That ordering matters because many behavior checks are threshold checks: a rule can ask whether the active edition is at least Rust 2018, Rust 2021, Rust 2024, or the future edition. The compiler also exposes DEFAULT_EDITION as Rust 2015 and LATEST_STABLE_EDITION as Rust 2024, matching the official documentation's backward-compatibility rule that absence of an edition key falls back to 2015.
The EditionFuture variant is significant for contributors. It is not a user-facing stable edition; it exists so edition-related behavior can be implemented and tested before it is assigned to a concrete year edition. The source comments state that this future edition is permanently unstable and that associated features must also be feature gated. That lets compiler developers model an edition boundary early, write compatibility lints, and exercise tests without promising that a particular experimental behavior is part of a released edition. For readers migrating application crates, the stable set remains the year-based editions described by the Edition Guide.
Sources: compiler/rustc_span/src/edition.rs
rust-analyzer has its own minimal Edition enum in src/tools/rust-analyzer/crates/edition/src/lib.rs. It covers the stable year editions from 2015 through 2024 and deliberately lives in a separate crate because rust-analyzer uses edition data in both actual analyzer code and generated code. The enum is represented as u8, starts at zero, and keeps consecutive discriminants because syntax context code depends on that layout. Its public helpers mirror compiler needs at editor scale: parse strings such as 2021, display an edition as its year, iterate over all editions, and ask whether an edition is at least 2018, 2021, or 2024.
The separate rust-analyzer package is a good example of the repository's edition design discipline. Editor tooling needs to understand edition-sensitive syntax and name resolution even when it is not invoking the full compiler driver for every keystroke. Packaging the support code as the edition crate gives rust-analyzer one small, shared authority for edition parsing and comparisons. Its Cargo.toml describes it plainly as Rust edition support for rust-analyzer, and the empty dependency list reinforces that the concept is intentionally lightweight rather than a large subsystem.
Sources: src/tools/rust-analyzer/crates/edition/src/lib.rs, src/tools/rust-analyzer/crates/edition/Cargo.toml
System-to-Code Mapping
| Reader-facing concept | Repository representation | Why it matters |
|---|---|---|
| Stable editions | Edition2015, Edition2018, Edition2021, Edition2024 | These are the editions a normal crate can select today. |
| Default edition | DEFAULT_EDITION and rust-analyzer Edition::DEFAULT | Both default to 2015 for backward compatibility. |
| Latest stable edition | LATEST_STABLE_EDITION, rust-analyzer LATEST, rust-analyzer CURRENT | These identify Rust 2024 as the current stable edition in the supplied source. |
| Future edition work | EditionFuture and compiletest Future | Contributors can test upcoming edition behavior before it becomes a concrete year edition. |
| Compatibility lint families | lint_name() values such as rust_2024_compatibility | Migration tooling can group warnings by target edition. |
| Tool style behavior | rustfmt StyleEditionDefault | Formatting defaults can change by style edition without changing language semantics. |
The compiler's lint_name method turns an edition into the name of a compatibility lint group, such as rust_2018_compatibility, rust_2021_compatibility, or rust_2024_compatibility. This is the source-level counterpart to the Edition Guide's migration story. When moving a crate forward, users need warnings about code that remains valid in an older edition but will be interpreted differently or discouraged in the target edition. By giving each edition an associated lint-group name, compiler diagnostics and migration tooling have a stable vocabulary for reporting edition-specific compatibility issues.
The is_stable method draws the boundary between editions users can select as stable language modes and the future edition used for development. This distinction keeps the public contract clear. Rust can land implementation support for upcoming edition rules without exposing that mode as stable, and tests can still compare ordinary year editions with the future marker because all variants participate in ordering. When contributors add a new edition, source comments require updating the complete edition list, the user-facing edition-name list, session helper functions, and Cargo's edition enum as well, making edition support a cross-toolchain coordination task rather than a one-file change.
Sources: compiler/rustc_span/src/edition.rs
Edition-Dependent Execution Flow
Edition selection becomes visible when source spans and macro expansion need to choose behavior. compiler/rustc_builtin_macros/src/edition_panic.rs implements this for panic! and unreachable!. The expansion functions choose either panic_2015 or panic_2021, and either unreachable_2015 or unreachable_2021, based on whether the relevant span is at least Rust 2021. The result is still an ordinary macro call into std::panic or core::panic, but the selected internal macro reflects the edition semantics expected at the user's call site.
The call-site part is crucial. The helper walks outward through expansion context and skips macro definitions that were allowed to use the internal unstable edition_panic feature. That avoids accidentally using the edition of an implementation macro such as an assertion macro definition instead of the edition of the user code that triggered the expansion. In other words, edition behavior follows the source location whose semantics the user expects, not necessarily the crate that contains a helper macro. This preserves the promise that editions are a property of the crate's code while still allowing standard-library macros to share implementations across editions.
Sources: compiler/rustc_builtin_macros/src/edition_panic.rs
For rust-analyzer, the execution flow is less about final code generation and more about maintaining a correct editing model. The analyzer must parse manifests or project metadata, convert edition strings into enum values, and apply edition thresholds while understanding syntax. Its FromStr implementation accepts only the stable year strings present in the enum and returns a structured parse error for invalid input. Its Display implementation writes the canonical year. Those small APIs matter because language-server features such as parsing, assists, and diagnostics need edition-aware behavior that agrees with the compiler even before a full build runs.
Testing and Tooling Signals
Compiletest carries its own edition parser because the Rust test suite needs to express edition-specific expectations in test directives. src/tools/compiletest/src/edition.rs models an edition as either Year(u32) or Future. The comment says ordering is load-bearing because the future edition must compare greater than any year-based edition. Its parser trims input, accepts the literal future, or parses a numeric year and emits a fatal test harness error if the value does not look like an edition. This keeps tests flexible enough to mention upcoming edition behavior without requiring every test harness directive to depend on the compiler's exact enum shape.
Sources: src/tools/compiletest/src/edition.rs
rustfmt shows a related but distinct concept: a style edition. Formatting defaults can evolve on edition-like boundaries even when the underlying Rust language edition is a separate compiler concept. src/tools/rustfmt/src/config/style_edition.rs defines a StyleEditionDefault trait and a style_edition_default! macro. One macro form returns the same default for every style edition. Another form returns one value for style editions 2015 through 2021 and a different value for 2024 and 2027. The tests demonstrate both cases, making the intended grouping explicit.
This matters because users experience editions through tooling as well as through the compiler. A project that opts into newer Rust idioms often expects formatter output, lints, diagnostics, and editor suggestions to line up with that choice. At the same time, rustfmt's style edition mechanism avoids conflating formatting policy with language parsing. A style default can change at a 2024 boundary and continue into a planned 2027 style edition without implying that the compiler has a stable Rust 2027 language edition. The repository keeps these concepts adjacent but not identical.
Sources: src/tools/rustfmt/src/config/style_edition.rs
Practical Guidance for Readers and Contributors
If you are using Rust, treat the official Edition Guide as the migration guide and the Book appendix as the short conceptual explanation. Check the edition key in your package manifest to know which language edition applies to each crate. If a key is absent, the compiler default represented in source is Rust 2015 for compatibility. When migrating, expect tooling to surface compatibility lints grouped by target edition, and remember that dependencies can remain on other editions because editions do not split the ecosystem.
If you are contributing to the compiler or first-party tools, start from the compiler enum and then check each tool-specific edition representation. A new edition or future-edition experiment is not complete when one enum variant compiles. The compiler source itself calls out coordinated updates such as the all-editions list, edition-name list, session helpers, and Cargo-side representation. Then inspect macro expansion, parser behavior, rust-analyzer support, compiletest directives, rustfmt style defaults, and migration lints for places where threshold helpers or explicit edition matches are required.
Good next steps are to read the official Edition Guide for user-facing migration details, then inspect compiler/rustc_span/src/edition.rs before touching edition-sensitive compiler behavior. For editor-facing work, read the rust-analyzer edition crate first because it is intentionally small and shows the stable public shape used by analyzer code. For formatting changes, keep language editions and style editions separate. For tests, use compiletest's numeric or future parser intentionally so that test expectations express the edition boundary being exercised rather than relying on incidental defaults.