Ein tiefer Einblick in Pattern Matching in Rust
Emily Parker
Product Engineer · Leapcell

Einleitung
In Rust ist Pattern Matching eine mächtige Sprachfunktion, die es uns ermöglicht, verschiedene Operationen basierend auf verschiedenen Mustern durchzuführen. Pattern Matching kann in verschiedenen Szenarien eingesetzt werden, wie z.B. bei der Arbeit mit Enum-Typen, der Destrukturierung von Tupeln und Strukturen, der Behandlung bedingter Ausdrücke und mehr. Dieser Artikel bietet eine detaillierte Einführung in die Syntax des Pattern Matchings in Rust und demonstriert seine Verwendung und Vorteile anhand von Beispielcode.
Grundlegende Verwendung
Rust verwendet das Schlüsselwort match
für Pattern Matching. Ein match
-Ausdruck besteht aus mehreren Armen, die jeweils ein Muster und einen Codeblock enthalten, der ausgeführt wird, wenn das Muster übereinstimmt. Rust wertet die Arme der Reihe nach aus und führt den Block aus, der dem ersten übereinstimmenden Muster entspricht. Hier ist ein einfaches Beispiel:
fn main() { let number = 3; match number { 1 => println!("One"), 2 => println!("Two"), 3 => println!("Three"), _ => println!("Other"), } }
Im obigen Code definieren wir eine Variable number
und weisen ihr den Wert 3 zu. Dann verwenden wir einen match
-Ausdruck, um number
abzugleichen. Zuerst prüft Rust den ersten Arm mit dem Muster 1
; da number
nicht 1 ist, wird dieser Arm übersprungen. Er fährt mit dem zweiten Arm, 2
, fort, der ebenfalls nicht übereinstimmt. Schließlich prüft er den dritten Arm, 3
, der übereinstimmt, also führt er den Block aus und gibt Three
aus.
Wenn keines der Muster übereinstimmt, dient der abschließende Unterstrich _
als Standardfall, ähnlich wie default
in anderen Sprachen, und führt den entsprechenden Block aus.
Matching von Enum-Typen
In Rust sind Enums ein Typ, mit dem Sie einen Wert definieren können, der eine von mehreren verschiedenen Varianten sein kann. Pattern Matching ist eine der häufigsten Möglichkeiten, mit Enums umzugehen, sodass wir je nach Variante unterschiedliche Logiken ausführen können.
Betrachten Sie das folgende Beispiel, in dem wir ein Enum namens Message
mit drei verschiedenen Varianten definieren: Move
, Write
und ChangeColor
:
enum Message { Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }
Nun verwenden wir einen match
-Ausdruck, um die verschiedenen Varianten von Message
zu behandeln:
fn process_message(msg: Message) { match msg { Message::Move { x, y } => println!("Move to coordinates (x={}, y={})", x, y), Message::Write(text) => println!("Write: {}", text), Message::ChangeColor(r, g, b) => println!("Change color to (r={}, g={}, b={})", r, g, b), } } fn main() { let msg1 = Message::Move { x: 10, y: 20 }; let msg2 = Message::Write(String::from("Hello, world!")); let msg3 = Message::ChangeColor(255, 0, 0); process_message(msg1); process_message(msg2); process_message(msg3); }
Im obigen Code definieren wir eine Funktion process_message
, die ein Message
-Enum als Parameter akzeptiert. Innerhalb des match
-Ausdrucks behandeln wir verschiedene Enum-Varianten mit unterschiedlicher Logik. Für die Message::Move
-Variante destrukturieren wir das Muster, um x
und y
zu erhalten, und geben dann die Koordinaten aus. Für Message::Write
geben wir die Zeichenkette direkt aus. Für Message::ChangeColor
destrukturieren wir r
, g
und b
und geben die RGB-Werte aus.
In der Funktion main
erstellen wir drei verschiedene Message
-Werte und übergeben sie zur Verarbeitung an process_message
. Basierend auf der Variante führen wir unterschiedliche Logiken aus.
Destrukturieren und Matchen von Strukturen
Neben Enums unterstützt Rust auch das Destrukturieren und Matchen von Strukturen. Eine Struktur ist ein benutzerdefinierter Datentyp, der aus mehreren Feldern besteht. Wir können Muster verwenden, um Strukturen zu destrukturieren und Operationen basierend auf Feldwerten auszuführen.
Betrachten Sie das folgende Beispiel, in dem wir eine Struktur namens Point
definieren, um einen Punkt in einem 2D-Raum darzustellen:
struct Point { x: i32, y: i32, }
Nun verwenden wir einen match
-Ausdruck, um verschiedene Point
-Strukturen zu destrukturieren und zu matchen:
fn process_point(point: Point) { match point { Point { x, y } => println!("Point coordinates: x={}, y={}", x, y), } } fn main() { let p1 = Point { x: 10, y: 20 }; let p2 = Point { x: -5, y: 15 }; process_point(p1); process_point(p2); }
Im obigen Code definieren wir eine Funktion process_point
, die einen Point
als Parameter akzeptiert. Im match
-Ausdruck verwenden wir das Muster Point { x, y }
, um die Felder der Struktur zu destrukturieren und auszugeben.
In der Funktion main
erstellen wir zwei verschiedene Point
-Instanzen und übergeben sie an process_point
. Pattern Matching erleichtert den Zugriff auf Strukturfelder und deren Bearbeitung.
Vereinfachung des Matchings mit if let
In einigen Fällen ist uns nur wichtig, ob ein bestimmtes Muster übereinstimmt, und wir müssen keine anderen Muster behandeln. In solchen Fällen kann der if let
-Ausdruck den Matching-Prozess vereinfachen.
Betrachten Sie das folgende Beispiel, in dem wir ein Enum namens Value
mit zwei Varianten definieren: Number
und Text
:
enum Value { Number(i32), Text(String), }
Nun verwenden wir if let
, um zu prüfen, ob eine Value
-Instanz eine Number
ist:
fn main() { let value = Value::Number(42); if let Value::Number(n) = value { println!("The value is a number: {}", n); } else { println!("The value is not a number"); } }
Im obigen Code definieren wir eine Value
-Variable und weisen ihr Value::Number(42)
zu. Dann verwenden wir if let
, um zu prüfen, ob es sich um die Number
-Variante handelt. Wenn ja, destrukturieren wir die Zahl und geben sie aus; andernfalls geben wir eine Meldung aus, die besagt, dass es sich nicht um eine Zahl handelt.
Die Verwendung von if let
kann den Code prägnanter und lesbarer machen, insbesondere wenn nur ein Muster von Interesse ist.
Matching mehrerer Muster
Manchmal möchten wir mehrere Muster abgleichen und denselben Codeblock ausführen. Rust bietet den Operator |
, um mehrere Muster in einem einzigen Arm abzugleichen.
Betrachten Sie das folgende Beispiel, in dem wir eine Variable number
definieren und mehrere Muster abgleichen:
fn main() { let number = 42; match number { 0 | 1 => println!("Zero or one"), 2 | 3 | 4 => println!("Two, three, or four"), _ => println!("Other"), } }
Im obigen Code verwenden wir match
, um number
auszuwerten. Der erste Arm gleicht sowohl 0 als auch 1 mit 0 | 1
ab. Der zweite Arm gleicht 2, 3 und 4 mit 2 | 3 | 4
ab. Der abschließende Unterstrich _
dient als Standardfall für alle anderen Werte.
Der Operator |
hilft dabei, mehrere Werte sauber abzugleichen und Code-Duplizierung zu vermeiden.
if let
und while let
Zusätzlich zu match
bietet Rust if let
und while let
für bedingtes Pattern Matching.
Der if let
-Ausdruck führt ein Matching durch und führt einen Block aus, wenn die Bedingung wahr ist. Wenn er nicht übereinstimmt, geschieht nichts.
Der while let
-Ausdruck funktioniert wie if let
, wiederholt den Vorgang aber in einer Schleife, solange das Muster übereinstimmt.
Hier ist ein Beispiel, das beide demonstriert:
fn main() { let values = vec![Some(1), Some(2), None, Some(3)]; for value in values { if let Some(num) = value { println!("Number: {}", num); } else { println!("None"); } } let mut values = vec![Some(1), Some(2), None, Some(3)]; while let Some(value) = values.pop() { if let Some(num) = value { println!("Number: {}", num); } else { println!("None"); } } }
Im obigen Code definieren wir zunächst einen Vektor von Option
-Werten. Mit einer for
-Schleife und if let
prüfen wir, ob jedes Element Some
ist, und geben den Wert aus, oder geben "None" aus.
Als Nächstes definieren wir einen weiteren Vektor und verwenden while let
, um Elemente einzeln zu entfernen. Solange ein Element Some
ist, geben wir seinen Wert aus; andernfalls geben wir "None" aus.
Mit if let
und while let
können wir Bedingungen flexibel abgleichen und Muster behandeln.
Exhaustiveness Checking in match
In Rust sind match
-Ausdrücke erschöpfend. Das bedeutet, dass der Compiler prüft, ob alle möglichen Fälle in einem match
-Ausdruck behandelt wurden, um sicherzustellen, dass nichts übersehen wird.
Wenn ein match
-Ausdruck nicht erschöpfend ist, gibt der Compiler eine Warnung aus, um potenzielle Fehler zu vermeiden. Um die Erschöpfung sicherzustellen, können wir am Ende des match
einen _
-Arm als Fallback hinzufügen.
Hier ist ein Beispiel, das die Exhaustiveness-Prüfung demonstriert:
enum Color { Red, Green, Blue, } fn main() { let color = Color::Red; match color { Color::Red => println!("Red"), Color::Green => println!("Green"), // Missing the Color::Blue branch } }
Im obigen Code definieren wir ein Enum Color
mit drei Varianten. Dann verwenden wir einen match
-Ausdruck, um die Variable color
abzugleichen. Wir stellen Arme für Color::Red
und Color::Green
bereit, lassen aber Color::Blue
aus.
Wenn wir versuchen, diesen Code zu kompilieren, erzeugt der Rust-Compiler die folgende Warnung:
warning: non-exhaustive patterns: `Color::Blue` not covered
Diese Warnung weist darauf hin, dass unser match
-Ausdruck nicht erschöpfend ist, da er nicht alle möglichen Fälle behandelt.
Um dieses Problem zu beheben, können wir entweder einen _
-Branch hinzufügen oder alle Enum-Varianten explizit abgleichen.
Fazit
Pattern Matching ist eine mächtige und flexible Sprachfunktion in Rust, die es ermöglicht, basierend auf verschiedenen Mustern unterschiedliche Operationen durchzuführen.
Dieser Artikel stellte die grundlegende Verwendung von Pattern Matching in Rust vor, einschließlich des Matchings von Enums und Strukturen, der Vereinfachung der Matching-Logik mit if let
und while let
und der Sicherstellung der Exhaustiveness in match
-Ausdrücken, um alle möglichen Fälle zu behandeln.
Wir sind Leapcell, Ihre erste Wahl für das Hosting von Rust-Projekten.
Leapcell ist die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis:
Mehrsprachige Unterstützung
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung - keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Auto-Skalierung zur einfachen Bewältigung hoher Parallelität.
- Null Betriebsaufwand - konzentrieren Sie sich einfach auf das Erstellen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ