Einführung
Eine Struktur gruppiert Felder, die immer zusammengehören. Ein Enum modelliert einen Wert, der eine von mehreren Alternativen sein kann. Jede Alternative kann unterschiedliche Daten enthalten. Mit Rusts match-Ausdruck können Sie diese Alternativen anschließend ausdrücklich behandeln.
Sie vervollständigen ein Nachrichten-Enum, zerlegen die Koordinaten einer Variante und erweitern das Modell um eine neue Variante. Der Compiler zeigt dabei absichtlich, warum jede mögliche Variante behandelt werden muss, bevor der Nachrichtenprozessor ausgeführt werden kann.
Varianten mit gespeicherten Daten definieren
In diesem Schritt definieren Sie drei alternative Formen für einen einzigen Typ Message.
Wechseln Sie in das vorbereitete Projekt und öffnen Sie den Quelltext:
cd /home/labex/project/message-processor
nano src/main.rs
Eine Enum-Deklaration listet Varianten innerhalb geschweifter Klammern auf. Der Code erstellt eine Variante mit dem Namen des Enums, :: und dem Variantennamen. Verschiedene Varianten desselben Enums können unterschiedliche Arten von Daten enthalten.
Ersetzen Sie den Kommentar für Schritt 1 durch:
Text(String),
Move { x: i32, y: i32 },
Stop,
Text(String) enthält einen unbenannten Wert. Rust bezeichnet diese Form als tuple-artige Variante. Move { x, y } enthält benannte Felder und wird daher strukturartige Variante genannt. Stop enthält keine zusätzlichen Daten und heißt Einheitsvariante. Merken Sie sich zunächst lieber die Formen „ein unbenannter Wert, benannte Felder oder keine Daten“, statt sich sofort die formalen Begriffe einzuprägen. Trotz ihrer unterschiedlichen Formen hat jeder erstellte Wert den einheitlichen Typ Message.
Speichern Sie mit Ctrl+O, drücken Sie Enter und beenden Sie den Editor mit Ctrl+X. Prüfen und starten Sie den vorbereiteten Prozessor:
cargo check
cargo run --quiet
Die Ausgabe sollte wie folgt aussehen:
Text: Maintenance at 9
Move received
Stop requested
Das vorbereitete match wählt für jede Variante einen Arm aus. Jeder Arm hat die Form pattern => behavior; kurze Arme werden durch Kommas getrennt. Das Pattern Text(text) benennt den gespeicherten String. Der Move-Arm benennt außerdem x und y und fasst sie anschließend vorübergehend als (x, y) zusammen. Die Klammern erstellen hier ein Tupel mit zwei Werten, das nur als vorbereitete Struktur dient; Tupel sind in diesem Lab kein Lernziel. Der Name _coordinates beginnt mit _. Damit teilen Sie Rust mit, dass der absichtliche Verzicht auf die Verwendung dieses vorübergehenden Werts beabsichtigt ist. So bleibt der erste Lauf warnungsfrei. Im nächsten Schritt ersetzen Sie diese Struktur und verwenden beide Koordinaten in der sichtbaren Ausgabe.
Daten aus Varianten zerlegen und verwenden
In diesem Schritt ändern Sie den Move-Arm des match, sodass die von seinem Pattern extrahierten Koordinaten in der Ausgabe erscheinen.
Patterns können die Variante, auf die sie zutreffen, in ihre Bestandteile zerlegen. Bei einer strukturartigen Variante erzeugt das Schreiben der Feldnamen Bindings, die der Ausdruck des Arms verwenden kann.
Öffnen Sie den Quelltext:
nano src/main.rs
Ersetzen Sie den vollständigen Move-Arm durch:
Message::Move { x, y } => println!("Move to ({x}, {y})"),
Wenn eine Move-Nachricht eintrifft, bindet das Pattern ihre gespeicherten Felder an die lokalen Namen x und y. Der Ausgabebefehl verwendet diese Namen; ein separater Feldzugriff oder eine Typumwandlung ist nicht erforderlich.
Speichern und beenden Sie den Editor. Führen Sie anschließend Folgendes aus:
cargo run --quiet
Die mittlere Zeile sollte nun lauten:
Move to (3, -2)
Der negative Wert von y zeigt, dass der Arm die von der tatsächlichen Variante gespeicherten Daten verwendet hat, anstatt feste Koordinaten auszugeben.
Ein vollständiges Match erweitern
In diesem Schritt fügen Sie eine Pause-Nachricht hinzu, beobachten die Vollständigkeitsprüfung des Compilers und behandeln anschließend die neue Alternative.
Ein match muss jeden möglichen Wert seines Enums abdecken. Wenn Sie eine Variante hinzufügen, können Sie anhand dieser Regel erkennen, bei welchen match-Ausdrücken Sie eine Entscheidung für den neuen Fall ergänzen müssen.
Öffnen Sie die Datei und fügen Sie diese Variante direkt unterhalb von Stop ein:
nano src/main.rs
Pause(u32),
Speichern und beenden Sie den Editor. Prüfen Sie anschließend das Programm. Ein Fehler ist an dieser Stelle zu erwarten:
cargo check
Suchen Sie den unveränderlichen Teil der Diagnose:
error[E0004]: non-exhaustive patterns: `Message::Pause(_)` not covered
Der Compiler verweist auf match message, weil das Enum jetzt einen Pause-Wert enthalten kann, aber kein Arm festlegt, was damit geschehen soll.
Öffnen Sie den Quelltext erneut. Fügen Sie diesen Arm unterhalb des Stop-Arms ein:
Message::Pause(seconds) => println!("Pause for {seconds} seconds"),
Entfernen Sie anschließend in main die führenden // aus der vorbereiteten Zeile process(Message::Pause(5));. Das Pattern gibt dem gespeicherten u32-Wert den lokalen Namen seconds.
Speichern und beenden Sie den Editor. Prüfen und starten Sie das Programm:
cargo check
cargo run --quiet
Die vollständige Ausgabe sollte lauten:
Text: Maintenance at 9
Move to (3, -2)
Stop requested
Pause for 5 seconds
Die erfolgreiche Prüfung zeigt, dass das match wieder vollständig ist. Die vierte Zeile zeigt, dass der neue Arm die Pausendauer aus Pause zerlegt hat.
Zusammenfassung
Sie haben Einheitsvarianten, tuple-artige und strukturartige Enum-Varianten definiert, gespeicherte Daten mithilfe von Match-Patterns zerlegt und den Compilerfehler E0004 verwendet, um ein match nach dem Hinzufügen einer neuen Alternative wieder vollständig zu machen.


