Editions

From Rust Community Wiki

The concept of editions was introduced in 2018 to allow stability without stagnation[1]. They provide a way to make changes to the language that would otherwise be backwards incompatible, which would violate Rust's stability guarantees. Editions permit changes to syntax and semantics, but not the standard library of Rust. Because editions are opt-in on a per-crate basis and can interoperate with each other, there is no risk of an ecosystem split.

Technical details[edit]

When compiling a crate, the Rust compiler transforms the code into different intermediate representations (IRs) before generating the final machine code. The AST (abstract syntax tree) is first transformed to the HIR (high-level IR), then the MIR (mid-level IR) and then the LLVM IR. While the AST and HIR depends on the edition (for example, dyn is a valid identifier only in the 2015 edition), the same MIR is used for all editions. This is what allows different editions to interoperate seamlessly.

One compiler can compile all editions; the edition is just a compiler switch, comparable to the optimization level. Editions should not be compared with language versions (e.g. C++17 and C++20).

Current editions[edit]

Currently there are three Rust editions:

  • Rust 2015, named after the release year of Rust 1.0, is the default edition, which is used if no edition is specified.
  • Rust 2018 is available since Rust version 1.31. All versions since then support both edition 2015 and 2018. This means that crates using the 2018 edition can depend on crates using the 2015 edition and vice versa.
  • Rust 2021 is available since Rust version 1.56. All versions since then support edition 2015, 2018 and 2021, so crates using these versions can be freely mixed.

Specifying the edition[edit]

When using cargo, the edition is specified in the Cargo.toml file with the edition key:

edition = "2021"

When a new project is created with cargo new, cargo automatically generates a Cargo.toml file using the most recent edition (currently 2021).

When invoking rustc manually, the --edition option should be used:

$ rustc src/main.rs --edition 2021

Timeline of the 2018 edition[edit]

Developing and releasing the 2018 edition involved a lot of planning and work from many parts of the Rust community. Here's a summary of the events:

  • In June 2017, [_ |-}}.html RFC 2052: epochs] proposing the concept of editions was published.
  • In September 2017, the RFC was accepted.[2]
  • In March 2018, the plans to make a 2018 edition were announced for the first time on the Rust blog in the 2018 roadmap.[3]
  • In September, Rust 1.29 was released, which shipped the cargo fix command to automatically migrate a project to the 2018 edition.[4]
  • In October, Rust 1.30 was released, which shipped raw identifiers, so newly introduced keywords can be escaped.[5]
  • In November, the redesigned website went online.[6]
  • In December 2018, Rust 1.31, containing the 2018 edition, was released.[7]

What changed with Rust 2018[edit]

This is a summary. More information can be found in the edition guide and in the Rust 2018 announcement.

Changes specific to the 2018 edition[edit]

The following features were released in Rust 1.31, unless a different version is indicated, and are (or were initially) only available in the 2018 edition:

  • The async and await keywords were reserved
  • the try keyword was reserved and dyn became a strict keyword.
  • Module paths were reworked, to make them simpler and more intuitive.
  • Anonymous trait parameters were deprecated.
  • Non-lexical lifetimes were introduced to make borrow checking more correct, permissive and provide better error messages.
    • They were backported to the 2015 edition in Rust 1.36.
  • The macro_rules! operator ? (matching at most one item) was added in Rust 1.32
    • It was backported to to the 2015 edition in Rust 1.37.
  • async/await was stabilized in Rust 1.39.

Other changes associated with Rust 2018[edit]

These changes are available in the 2015 edition as well, but were advertised together with the above features, because they were released around the same time:

Language changes[edit]

  • Improved lifetime elision
  • const fn
  • Raw identifiers
  • Attribute- and function-like procedural macros
  • no_std applications

Tools[edit]

  • rustfmt, Rusts code formatter
  • clippy, Rusts linter

Governance[edit]

  • A new edition of the book "The Rust Programming Language" was released
  • The website was redesigned
  • The Domain Working Groups for Network Services, Command-line applications, WebAssembly and Embedded devices were announced

What changed with Rust 2021[edit]

Unlike the 2018 edition, the 2021 edition was more limited in scope, and the Rust teams decided to not use the edition as a "rallying point" or to advertise new, unrelated features as part of the 2021 edition.

More information is available in the edition guide, in the Rust 2021 announcement and in the Rust 1.56 release announcement.

  • Additions to the prelude: TryIntoThis links to official Rust documentation, TryFromThis links to official Rust documentation and FromIteratorThis links to official Rust documentation were added to the default prelude
  • Default cargo feature resolver: Cargo uses the new feature resolver by default for Rust 2021 crates.
  • IntoIteratorThis links to official Rust documentation for arrays:
    • Arrays implement IntoIterator in all editions.
    • Calls to IntoIterator::into_iterThis links to official Rust documentation are hidden in Rust 2015 and Rust 2018 when using method call syntax (i.e., array.into_iter()). So, array.into_iter() still resolves to (&array).into_iter() as it has before.
    • array.into_iter() changes meaning to be the call to IntoIterator::into_iter in Rust 2021.
  • Disjoint closure captures:
    • Closures only capture the needed struct fields, not the whole struct, when possible.
    • For example, || a.x + 1 only captures a.x and not a.
  • Panic macro:
    • panic!(..) now always uses format_args!(..), just like println!().
    • This means that the first argument must be a string literal. To panic with other values, panic_any()This links to official Rust documentation must be used.
  • The syntax any_identifier#, any_identifier"...", and any_identifier'…' is now reserved.
  • Warnings promoted to errors: Trait objects without dyn and ... range patterns are no longer allowed.
  • Or-patterns in macro_rules:
    • The :pat metavariables can now match or-patterns (multiple patterns separated by |).
    • For the previous behavior, :pat_param can be used.

References[edit]