Skip to content

/flags/rust/rustc-edition

rustc --edition: compile with Rust 2015, 2018, 2021 or 2024

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · rustc 1.98.1 (48a229cea 2026-09-01)updated
tool
Rust
command
rustc
flag
--edition <2015|2018|2021|2024>
default
2015 for rustc; cargo new writes the latest edition into Cargo.toml

rustc --edition 2021 main.rs compiles the file with the rules of the Rust 2021 edition. Editions are opt-in language versions: each one can reserve new keywords and change a few defaults, while crates of different editions still link together. Plain rustc defaults to the 2015 edition, whereas Cargo passes the edition from Cargo.toml, so the same code can compile under Cargo and fail with rustc file.rs, or the other way round.

A keyword that depends on the edition

async became a keyword in 2018 and gen is reserved in 2024:

bash
cat > old.rs <<'EOF'
fn main() {
    let async = 1;
    println!("async = {}", async);
}
EOF
cat > new.rs <<'EOF'
fn main() {
    let gen = 5;
    println!("gen = {gen}");
}
EOF
echo "== old.rs, default edition"
rustc old.rs && ./old
echo "== old.rs, --edition 2018"
rustc --edition 2018 old.rs; echo "exit: $?"
echo "== new.rs, --edition 2021"
rustc --edition 2021 new.rs && ./new
echo "== new.rs, --edition 2024"
rustc --edition 2024 new.rs 2>&1 | sed -n "1,11p"; echo "exit: ${PIPESTATUS[0]}"
echo "== invalid value"
rustc --edition 2027 new.rs; echo "exit: $?"

Output

== old.rs, default edition
async = 1
== old.rs, --edition 2018
error: expected identifier, found keyword `async`
 --> old.rs:2:9
  |
2 |     let async = 1;
  |         ^^^^^ expected identifier, found keyword
  |
help: escape `async` to use it as an identifier
  |
2 |     let r#async = 1;
  |         ++

error: expected one of `move`, `use`, `{`, `|`, or `||`, found `<eof>`
 --> old.rs:3:28
  |
3 |     println!("async = {}", async);
  |                            ^^^^^ expected one of `move`, `use`, `{`, `|`, or `||`

error: aborting due to 2 previous errors

exit: 1
== new.rs, --edition 2021
gen = 5
== new.rs, --edition 2024
error: expected identifier, found reserved keyword `gen`
 --> new.rs:2:9
  |
2 |     let gen = 5;
  |         ^^^ expected identifier, found reserved keyword
  |
help: escape `gen` to use it as an identifier
  |
2 |     let r#gen = 5;
  |         ++

exit: 1
== invalid value
error: argument for `--edition` must be one of: <2015|2018|2021|2024|future>. (instead was `2027`)

exit: 1

The 2015 code that uses async as a variable name builds with the default edition and fails from 2018 on; rustc suggests the raw identifier r#async. In the same way gen is fine in 2021 and an error in 2024.

Editions at a glance

EditionStable sinceExamples of what changed
2015Rust 1.0Default for rustc; extern crate needed for dependencies
2018Rust 1.31async, await, dyn and try reserved; paths start with crate:: or a crate name; no extern crate
2021Rust 1.56Disjoint closure captures, IntoIterator for arrays by value, new prelude (TryFrom, TryInto), panic! always treats its first argument as a format string
2024Rust 1.85gen reserved, unsafe extern blocks, RPIT lifetime capture rules, tail expression temporary scope

The full list for each edition is in the Rust Edition Guide. rustc --edition future exists for compiler testing and requires a nightly toolchain.

rustc versus Cargo

Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2024"

Cargo turns the edition field into --edition 2024 for every rustc call of that crate. When you compile a single file outside Cargo (see compile a single file with rustc), pass the edition yourself, otherwise you get 2015 behaviour: use paths resolve differently, dyn is a warning instead of required, and keywords such as async are ordinary identifiers. A project made with cargo new already has the latest edition set.

Pitfalls

  • An edition does not choose the compiler version: --edition 2024 with a rustc older than 1.85 fails with an error listing the editions it knows, as the invalid value above does.
  • Mixed editions in one workspace are normal; set edition in each member's Cargo.toml or once in [workspace.package].
  • rustfmt has its own edition setting; if formatting rejects valid code, pass the same edition to rustfmt --edition.

FAQ

What edition does rustc use without --edition?

2015. Plain rustc keeps the oldest edition for compatibility, while cargo new writes the newest edition into Cargo.toml. That is why a file that builds with cargo can fail with rustc file.rs.

Can crates of different editions be used together?

Yes. The edition is chosen per crate, and a 2024 crate can depend on 2015 or 2018 crates. Raw identifiers such as r#async let code in a newer edition call items whose names became keywords.

How do I move a project to a newer edition?

Run cargo fix --edition, which applies the automatic migrations while the old edition is still set, then change the edition field in Cargo.toml and build again. The Edition Guide lists the changes per edition.

Sources