Eine Rust-Edition ist eines der ungewöhnlichsten Konzepte in der Sprache: ein opt-in mechanismus, mit dem das Rust-Team alle paar Jahre inkompatible Sprach-Änderungen einführen kann, ohne damit irgendeinen existierenden Code zu brechen. Crates in verschiedenen Editionen können beliebig miteinander verlinkt werden — der Compiler bleibt derselbe, nur die Parsing- und Resolution-Regeln verschieben sich pro Crate. Dieser Artikel erklärt, was eine Edition ist (und was sie ausdrücklich nicht ist), zeigt die Highlights jeder bisher veröffentlichten Edition und führt durch den Migrations-Workflow.
Was eine Edition ist — und was nicht
Eine Edition ist kein Versions-Sprung der Sprache. Rust hat keine „Rust 2" — es gibt nur Rust 1.x mit unterschiedlichen Editions. Eine Edition ist im Wesentlichen eine Konfiguration, die pro Crate festlegt, wie der Compiler den Quelltext interpretiert.
Das Compatibility Promise von Rust (seit 1.0 in 2015) sagt: Code, der einmal kompiliert, kompiliert für immer. Eine Sprache, die das ernst nimmt, hat aber ein Problem — sie kann nie syntaktisch oder semantisch inkompatible Änderungen einführen. Genau dafür sind Editionen erfunden worden.
Editionen erlauben:
- Neue Schlüsselwörter einzuführen.
asyncundawaitsind seit Edition 2018 reservierte Wörter — in 2015-Code dürfen sie noch als Identifier vorkommen. - Bestehende Syntax umzudeuten. Die Capture-Regeln für Closures in der 2021-Edition fangen Variablen feiner ein als 2018.
- Defaults umzustellen.
panic = "unwind"vs."abort", Lint-Strenge, Pattern-Match-Behavior — alles Dinge, die in einer Edition anders sein können.
Editionen ändern nicht:
- Die ABI oder das Compiler-Binary. Eine Toolchain weiß, wie sie alle Editionen baut.
- Die Standard-Library-API.
Vec,HashMap,Iterator— alles gleich in jeder Edition. - Die Performance. Es ist keine andere Sprache, sondern eine andere Parse-Konfiguration.
Das edition-Feld in Cargo.toml
Jedes Crate setzt seine Edition explizit:
[package]
name = "meine-app"
version = "0.1.0"
edition = "2024"Erlaubte Werte: "2015", "2018", "2021", "2024". Default beim Anlegen mit cargo new ist die zum Zeitpunkt der Crate-Erstellung aktuelle Edition — bei aktuellen Toolchains also "2024".
Wenn edition fehlt, fällt Cargo auf 2015 zurück. Das ist Legacy-Verhalten für sehr alte Projekte; modern erstellte Manifeste haben das Feld immer gesetzt.
Edition 2015 — die Basis
Rust 1.0 wurde am 15. Mai 2015 veröffentlicht. Alles davor zählt als „pre-edition". 2015 ist die implizite Default-Edition für Crates ohne edition-Feld.
Charakteristisch:
- Absolute Pfade beginnen mit
:::::std::collections::HashMap. extern crateist Pflicht, um eine Dependency überhaupt verfügbar zu machen.use-Pfade inmod-Blöcken verhalten sich anders als in späteren Editionen.
Modern wird 2015-Code praktisch nicht mehr geschrieben. Du begegnest ihm in alten Projekten oder Crates auf crates.io, die nie migriert wurden. Solche Crates bleiben aber problemlos benutzbar — siehe Interop-Abschnitt unten.
Edition 2018 — der große Sprung
Veröffentlicht am 6. Dezember 2018 mit Rust 1.31. Die ambitionierteste Edition, weil sie viele lang gewünschte Aufräum-Arbeiten in einem Schritt erledigte.
Wichtige Änderungen:
extern crateweitgehend abgeschafft. Dependencies ausCargo.tomlsind direkt peruseverfügbar — keine extra Deklaration nötig.- Neue Modul-Pfade.
crate::,self::,super::ersetzen das alte::-Prefix. Pfade ohne Prefix beziehen sich auf Items im Scope. asyncundawaitals reservierte Schlüsselwörter (auch wenn die volle Async-Implementierung erst 2019 stabil wurde).dyn Trait-Syntax für Trait-Objekte. Vorher schrieb manBox<Trait>, danachBox<dyn Trait>— der explizite Marker macht klar, dass es dynamischer Dispatch ist.impl Traitin Argument- und Rückgabe-Positionen als kompaktere Generics-Syntax.- Verbesserter Lifetime-Checker (NLL — Non-Lexical Lifetimes) machte viele frühere Borrow-Checker-Probleme verschwinden.
Beispiel für 2015 vs. 2018:
extern crate serde;
use ::std::collections::HashMap;
fn main() {
let m: HashMap<String, i32> = HashMap::new();
}use std::collections::HashMap;
fn main() {
let m: HashMap<String, i32> = HashMap::new();
}Die meisten Open-Source-Crates haben in den ersten zwei Jahren auf 2018 migriert. Wer heute irgendwo extern crate sieht, hat ein altes oder bewusst konservativ gehaltenes Projekt vor sich.
Edition 2021 — die feinen Schliffe
Veröffentlicht am 21. Oktober 2021 mit Rust 1.56. Eine deutlich kleinere Edition als 2018, mit gezielten Verbesserungen.
Wichtige Änderungen:
IntoIteratorfür Arrays.for x in [1, 2, 3] { ... }funktioniert jetzt ohne explizites.iter(). Vorher musstest du[1, 2, 3].iter()schreiben, weil Array-by-Value-Iteration nicht imIntoIterator-Impl von Arrays existierte.- Disjoint Closure Captures. Closures fangen einzelne Felder eines Structs ein, nicht den ganzen Struct. Ermöglicht engere Borrows in Closures, die sonst den Borrow Checker irritiert hätten.
- Neue Macro-
pat-Fragment-Spezifikation für Pattern-Matching in deklarativen Makros. - Panic-Macro-Diagnostik.
panic!("error: {}", x)darf im Format-String die Variable nicht mehr stillschweigend als positional behandeln — explizite Argumente sind ab 2021 Pflicht. - Reserved Prefixes:
prefix"string"undprefix'c'sind ab 2021 reserviert, damit künftige Editionen sie verwenden können (vergleichbar mit demb"bytes"-Prefix).
Beispiel für Disjoint Captures:
struct Punkt { x: i32, y: i32 }
fn main() {
let mut p = Punkt { x: 0, y: 0 };
let mut set_x = |val| p.x = val;
// p.y kann hier ohne Konflikt mit set_x verwendet werden,
// weil die Closure NUR p.x captured — nicht ganz `p`.
let read_y = || p.y;
set_x(5);
println!("y = {}", read_y());
}Vor 2021 hätte set_x ganz p als &mut-Borrow gehalten — read_y daneben gleichzeitig wäre ein Borrow-Konflikt gewesen.
Edition 2024 — Aufräum-Arbeiten
Veröffentlicht im Frühjahr 2024 mit Rust 1.85. Wie 2018 eine Sammlung kleinerer Änderungen, die Ecken der Sprache aufräumen.
Wichtige Änderungen:
- Veränderte Lifetime-Capture-Regeln für
impl Trait.fn foo() -> impl Traitcapturt jetzt automatisch alle Lifetimes der Argumente — das ergibt sicherere Defaults für asynchronen Code und Iterator-Adaptern. async fnin Traits ist stabil und idiomatisch (überimpl Trait-Mechanik im Hintergrund).let chainsinif let/while let— verkette Bedingungen mit&&:
if let Some(user) = get_user()
&& let Some(name) = user.name()
&& name.starts_with("A")
{
// Nur wenn alle drei Bedingungen zutreffen.
}genals reserviertes Schlüsselwort für künftige Generator-Syntax.- Aufgeräumte Unsafe-Regeln:
unsafe-Blöcke werden inextern-Blöcken zur Pflicht. if let-Temporary-Scoping verändert: Bindings in einerif let-Bedingung leben jetzt nur noch im jeweiligen Branch, nicht im danach folgendenelse.- Tail-Expression-Drop-Order in match-Armen einheitlicher.
Die 2024-Edition ist der Standard für neue Projekte zum Zeitpunkt dieses Artikels.
Interop zwischen Editionen
Ein zentrales Versprechen: Crates verschiedener Editionen funktionieren nahtlos zusammen.
Du kannst in einem 2024-Crate problemlos eine Dependency nutzen, die intern noch 2015-Code ist. Du kannst eine Library auf 2021 belassen und sie aus einem 2024-Binary nutzen. Cargo weiß, mit welcher Edition jedes Crate kompiliert werden muss — er ruft rustc mit dem entsprechenden Flag auf.
Das bedeutet konkret:
- Crate-Autoren können auf 2018 bleiben, ohne dass ihre Library „veraltet" wirkt. Die Sprache, die sie verwenden, hat sich nur an der Oberfläche geändert.
- Du kannst dein Projekt heute auf 2024 bringen, ohne dass es deine Dependencies zwingt zu migrieren.
- Migration ist ein internes Refactoring, keine API-Änderung.
Migrations-Workflow mit cargo fix
Cargo bringt einen automatischen Edition-Migrator mit. Der typische Workflow:
# 1. In die nächste Edition migrieren (von 2021 zu 2024).
cargo fix --edition
# 2. Cargo.toml ändern: edition = "2024"
# (cargo fix --edition macht das automatisch)
# 3. Testen, dass alles noch baut und Tests laufen.
cargo build
cargo test
# 4. Optional: idiomatische Cleanups (von cargo fix vorgeschlagen).
cargo fix --edition-idiomscargo fix --edition ist sehr konservativ — es ändert nur Dinge, die syntaktisch nötig sind, damit der Code in der neuen Edition kompiliert. Idiomatische Verbesserungen (z. B. extern crate entfernen) holst du dir mit --edition-idioms.
Best Practices bei der Migration
- Immer in einem eigenen Branch migrieren — dann kannst du den Diff sauber reviewen.
- Versionskontrolle vor dem Fix.
cargo fixändert Dateien in-place. - Tests sind dein Sicherheitsnetz. Wenn Tests vor der Migration grün waren und danach immer noch grün sind, hat dich der Migrator wahrscheinlich nicht in eine Falle laufen lassen.
- Schrittweise migrieren. Von 2015 direkt auf 2024 ist möglich, aber empfehlenswerter ist 2015 → 2018 → 2021 → 2024, weil jeder Schritt einzeln verifizierbar ist.
- Workspaces —
cargo fix --editionmigriert nur das aktuelle Crate, nicht das ganze Workspace-Suite. Du musst pro Member-Crate gesondert migrieren oder mit--workspacearbeiten.
Wann migrieren?
Ein zentrales Element des Edition-Konzepts: es gibt keine Pflicht zur Migration. Ein Crate, das 2015 oder 2018 nutzt, ist nicht „veraltet" — es nutzt nur eine andere Konfiguration des Compilers.
Sinnvolle Migrations-Gründe:
- Neue Sprach-Features, die nur in einer höheren Edition verfügbar sind (z. B.
let chainsab 2024). - Idiomatische Hygiene — neue Codebase, sauberer Start mit der neuesten Edition.
- Schöne Compiler-Defaults — z. B. die feineren Closure-Captures der 2021-Edition.
Kein Grund:
- Versions-Druck. Die Sprache zwingt dich zu nichts.
- „Sieht moderner aus." Lesbarkeit oder Funktionalität ändert sich für Library-Konsumenten nicht.
Häufige Stolperfallen
edition = "2024" zwingt eine bestimmte MSRV.
Die 2024-Edition braucht mindestens Rust 1.85. Wer in Cargo.toml rust-version = "1.70" setzt und gleichzeitig edition = "2024" will, hat einen Widerspruch — Cargo wird sich beim Build mit einer Toolchain unter 1.85 beschweren. Faustregel: MSRV mindestens auf das Release setzen, in dem die jeweilige Edition stable wurde.
cargo fix --edition braucht einen sauberen Working Tree.
Das Tool verlangt, dass dein Git-Status uncommitted Änderungen entweder gestagt oder gestasht hat. Ansonsten verweigert es die Arbeit, um keine unvermischbaren Änderungen zu erzeugen. Mit --allow-dirty lässt sich das umgehen — Vorsicht, dann sind eigene und maschinelle Änderungen vermischt.
Panic-Format-Strings sind in 2021 strenger.
panic!(my_var) mit nur einer Variable wurde vor 2021 als „formatiere als Debug" interpretiert. Ab 2021 ist das ein Hinweis vom Compiler — panic!("{}", my_var) ist das, was du meintest. Beim Migrieren findet cargo fix --edition solche Stellen meist automatisch.
Disjoint Closure Captures verändern Drop-Reihenfolgen.
Wenn deine Closure ein RAII-Objekt (z. B. ein File-Handle, ein MutexGuard) capturet, kann die feinere Capture-Logik der 2021-Edition dafür sorgen, dass das Objekt früher oder später gedroppt wird als bisher. In seltenen Fällen führt das zu subtilen Verhaltens-Änderungen — bei kritischen Pfaden lohnt es sich, RAII-Guards sicherheitshalber explizit drop() zu lassen.
gen als reserviertes Schlüsselwort ab 2024.
Wer in altem Code eine Variable, Funktion oder ein Modul gen genannt hat, bricht beim Wechsel auf 2024. cargo fix --edition benennt diese Stellen mit dem Raw-Identifier-Präfix r#gen um, sodass der Code weiterhin korrekt funktioniert. Für neuen Code: andere Namen wählen.
extern crate bleibt für core und proc_macro relevant.
Auch in 2024 brauchst du extern crate proc_macro; in proc-macro-Crates und extern crate alloc; in no_std-Setups. Das gilt nur für genau diese eingebauten Crates. Für reguläre crates.io-Dependencies bleibt es seit 2018 unnötig.
Tail-Expression-Scope kann subtle Build-Bugs auslösen.
Die 2024-Edition ändert die Drop-Reihenfolge von Temporaries in match-Armen. Code, der versehentlich von einer bestimmten Drop-Reihenfolge abhing (z. B. ein MutexGuard, der „zufällig" zum richtigen Zeitpunkt freigegeben wurde), kann nach der Migration anders laufen. Tests sind hier Pflicht.
Weiterführende Ressourcen
Externe Quellen
- Rust Edition Guide
- Edition Guide – Migrating to 2024
- Edition Guide – Migrating to 2021
- Edition Guide – Migrating to 2018
- Rust Blog – Editions Overview
- The Cargo Book – Manifest
edition