Enums in Rust unterscheiden sich grundlegend von Enums in C/C++: Sie können assoziierte Daten speichern und eignen sich hervorragend zur Modellierung von Zuständen und Fehlern. Mit ihnen können typensichere finite Zustandsmaschinen, verschiedene Arten von Option/Result und das Muster „Sum Types“ erstellt werden. Historisch gesehen wurden ähnliche Konstruktionen in funktionalen Sprachen verwendet, um Varianten einer Entität mit streng getrennten Varianten zu beschreiben.
Problem: Ausdruckskraft zu erreichen (alle Varianten des Zustands auszudrücken), wobei jeder Verarbeitungsfall obligatorisch ist und keine Zweige versehentlich übersprungen werden können. Projektweite Fehler lassen sich ohne eine so ausdrucksstarke Struktur schwer typisieren.
Lösung: Enums mit assoziierten Daten und Pattern Matching bieten Kontrolle – jeder Zweig wird vom Compiler geprüft, die Vollständigkeit wird sichergestellt. Darüber hinaus sind bereits viele Hilfsmethoden für Result und Option implementiert.
Beispielcode:
enum NetworkState { Disconnected, Connecting(u32), // Versuch Nummer Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (attempt {})", count), NetworkState::Connected(addr) => println!("Net: connected to {}", addr), NetworkState::Error(msg) => println!("Net error: {}", msg), } }
Wichtige Merkmale:
Kann man enum-Zweige teilweise ohne _ verarbeiten?
Der Compiler untersagt nicht geschlossene Fälle für nicht erschöpfende Enums, aber wenn man _ verwendet, werden nicht verarbeitete Zweige „aufgenommen“. Es sollte vermieden werden, _ bei klinisch wichtigen Zweigen zu verwenden, damit zukünftige Änderungen nicht unbemerkt bleiben.
Wann werden assoziierte Werte referenziert und wann kopiert beim Pattern Matching?
Beim Pattern Matching werden assoziierte Daten standardmäßig verschoben (move). Wenn nur eine Ansicht benötigt wird, verwenden Sie Referenzen:
match &state { NetworkState::Connected(addr) => println!("by ref: {}", addr), _ => {} }
Kann man in einer Struktur zwei Enums mit sich überschneidenden Varianten im Namen verwenden?
Ja, aber die Namen der Varianten werden mit dem Prefix des Enums verwendet. Dies schließt Kollisionen aus und macht den Code selbstdokumentierend (z.B. Status::Ok vs NetworkState::Ok).
_ zur Verbergung neuer Varianten bei der Erweiterung von Enums._ =>) in kritischen Fehlerbehandlern.Im Code hat die Verarbeitung von Result<T, E> immer ein catch-all über _ =>, und neue Fehler (bei der Erweiterung von Enums) gehen vorbei – es kommt zu silent-loss von Fehlern.
Vorteile:
Nachteile:
Es wird exhaustiveness matching verwendet, jede Variante des Enums wird explizit verarbeitet, oder es gibt einen Panic in einem Zweig, für den kein robustes Verhalten vorliegt.
Vorteile:
Nachteile: