1. Match literals
In Rust, you can directly match specific literals. For example:
fn main() { let x = 1; match x { 1 => println!("Match to literal 1"), _ => println!("Other Values"), } }
whenx
The value of1
When the match is successful and the corresponding information is printed out. This writing method is very intuitive and effective for scenarios where specific values need to be processed.
2. Match named variables
In pattern matching, a named variable can be used to bind the matched value to a variable. It should be noted that inmatch
、if let
、while let
In other expressions, variables declared within the pattern willCover(shadow) External variable with the same name. For example:
fn main() { let x = Some(5); let y = 10; match x { None => println!("x is None"), Some(y) => println!("Match to Some,Inside y for: {}", y), } println!("External y: {}", y); }
In the above code,match
Appearing in the branchy
It's a brand new variable, boundSome
Internal value5
, while externaly
Still maintaining the original value10
. If you need to reference external variables when matching, you can use the match guard (see later).
3. Multi-pattern matching
Sometimes a matching branch needs to be processed for multiple values, and you can use the pipeline character.|
to represent the relationship of "or". For example:
fn main() { let x = 2; match x { 1 | 2 => println!("Match to 1 or 2"), _ => println!("Other Values"), } }
ifx
The value of1
or2
, execute the code of the corresponding branch, which makes the code more concise.
4. Match range (..=)
When matching a series of consecutive values, use..=
Operators can greatly simplify the code without listing each specific value. For example:
fn main() { let x = 3; match x { 1..=5 => println!("x is in the range of 1 to 5"), _ => println!("x out of range"), } }
Similarly, Rust supports characters (char
) Use range matching, but the range must be guaranteed to be non-empty, otherwise the compiler will report an error.
V. Deconstruction: Split complex data into separate parts
Rust's pattern matching is not only used for simple value matching, but can also be used to destructuring complex data types, such as structures, enums, tuples, etc.
1. Deconstruct structure
uselet
Statement, which can easily split fields in structures into different variables:
struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 10, y: 20 }; // Explicitly named let Point { x: a, y: b } = p; println!("a: {}, b: {}", a, b); // Abbreviation of structure fields (variable name is the same as field name) let Point { x, y } = p; println!("x: {}, y: {}", x, y); }
This deconstruction method allows us to easily manipulate data in the structure.
2. Deconstruct the enumeration
Each variant of an enum may contain different data. When deconstructing, you need to match the corresponding structure according to the definition of the enum. For example:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msg = Message::ChangeColor(255, 160, 0); match msg { Message::Quit => println!("Quit!"), Message::Move { x, y } => println!("Move to ({}, {})", x, y), Message::Write(text) => println!("Text message: {}", text), Message::ChangeColor(r, g, b) => println!("Change color to red: {}, green: {}, blue: {}", r, g, b), } }
By deconstructing the enumerated variant, further processing can be performed directly using its internal data.
3. Nested destruction
In practical applications, the data structure may be deeply nested, and we can also use pattern matching to deconstruct nested data. For example:
enum Color { Rgb(i32, i32, i32), Hsv(i32, i32, i32), } enum Message { ChangeColor(Color), // Other variants...} fn main() { let msg = Message::ChangeColor(Color::Rgb(0, 160, 255)); match msg { Message::ChangeColor(Color::Rgb(r, g, b)) => println!("Change the color to red: {}, green: {}, blue: {}", r, g, b), Message::ChangeColor(Color::Hsv(h, s, v)) => println!("Change the color using HSV: {}, {}, {}", h, s, v), _ => (), } }
This way allows us to be in onematch
Multiple nested data are processed simultaneously in expressions.
4. Mixed destruction of structure and tuple
Rust also supports mixed deconstruction of structures and tuples, enabling the splitting of complex data types into single raw values for processing. This flexibility is a major advantage of Rust in data processing.
6. Ignore the mode of value
In many cases, we don't need to use all the data we match, and Rust provides multiple ways to ignore unnecessary parts:
1. Use _ to ignore the entire value
When matching, if a branch does not care about the specific value, you can use it directly_
express:
fn main() { let x = 3; match x { _ => println!("Ignore specific values"), } }
In addition, it can also be used in function parameters_
to avoid warnings of unused variables.
2. Use _ Ignore part of the value in nesting
If you only care about some fields in a structure or enumeration, you can name only the required parts in the pattern, and use_
Ignore other parts. For example:
fn main() { let setting_value = Some(5); let new_setting_value = Some(5); match (setting_value, new_setting_value) { (Some(_), Some(_)) => println!("Cannot overwrite existing custom values"), _ => println!("Allow update settings"), } }
3. Name variables starting with _
If you need to bind a variable but it is not used for the time being, you can add it before the variable name_
, so Rust does not generate warnings about unused variables. However, it should be noted that underscores will not prevent the variable from obtaining ownership, but only indicates that the variable is not currently used.
4. Use .. Ignore the remaining parts
For data structures containing a large number of fields or elements, if you only focus on some of them, you can use..
To ignore the rest. For example, for structures:
struct Point3D { x: i32, y: i32, z: i32, } fn main() { let point = Point3D { x: 10, y: 20, z: 30 }; match point { Point3D { x, .. } => println!("Just care x Value of: {}", x), } }
It can be used similarly in tuples..
to match the head and tail parts, and ignore all elements in the middle. But it needs to be paid attention to..
The use of the ,must be unambiguous, otherwise the compiler will report an error.
7. Use the matching guard to add additional conditions
Sometimes pattern matching alone cannot meet complex conditions, such as matching a certain pattern, and further determining whether the value meets specific conditions. You can use match guard to add it after the patternif
condition:
fn main() { let num = Some(4); match num { Some(x) if x % 2 == 0 => println!("number {} It's an even number", x), Some(_) => println!("Match Some, but the guard conditions are not met"), None => println!("Match to None"), } }
Match guards can use variables bound in patterns and can be used in combination with multi-pattern matching, but it needs to be noted that guard conditions will affect the compiler's exhaustive check.
8. @ Binding: Binding at the same time as matching
@
Operators can bind values when matched, and at the same time check whether they meet a certain pattern. For example, when you need to match a value in a range and you want to get that value, you can use@
Bind:
fn main() { let id = 5; match id { id_variable @ 3..=7 => println!("Found the scope id: {}", id_variable), _ => println!("id is not within the range of 3 to 7"), } }
In the above code, ifid
The value of3..=7
Within the range, the value will be bound toid_variable
, convenient for subsequent use.
9. Summary
Rust's pattern matching syntax is not just for simple branch judgments, it allows us to:
- Exactly match literals and ranges,
- Get various parts of complex data through named variables and deconstruction.
- Flexible ignore unconcerned data,
- Add additional conditional limits by matching guards,
- And use
@
Binding implementation saves data while matching.
These features greatly improve the expressiveness and security of the code, allowing Rust to catch more errors during the compilation period and ensure the correctness of program behavior. I hope this article can help you better understand and apply pattern matching in Rust, providing a boost to writing more elegant and robust code!
This is the end of this article about in-depth understanding of pattern matching syntax in Rust (the latest recommendation). For more related Rust pattern matching syntax content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!