Schema syntax

Pattern matching is useful in many ways, such as let declaration variables, function parameters, match statements, if let, etc. The previous section only briefly covered some basic methods of using pattern matching. In this section, we will take a look at all the pattern matching methods and the powerful power of pattern matching

Match literals

The simplest ability to match literal types, such as the following i32 type:

let x = 1;
match x {
  1= >println!("1"),
  2= >println!("2"),
  3= >println!("3"), _ = >println!("Any value"), // match requires an exhaustive list of i32 types, _ to match all types
}
/ / 1
Copy the code

Matching named variables

This is usually used for deconstruction, and match creates a separate scope for the result of the match:

let x = Some(5);
let y = 10;
match x {
  Some(50) = >println!("x = 50"),

  // the y is in a separate scope, binding the value of Some, not the y outside
  Some(y) => println!("y = {:? }", y),
  _ => println!("Default x = {:? }", x),
}

// Here y is still 10
println!("After matching: x = {:? }, y = {:? }", x, y);
// y = 5
// Finally: x = Some(5), y = 10
Copy the code

Multiple patterns

Also support the use of | symbols, or the relationship between the said:

let x = 1;
match x {
  1 | 2= >println!("1 或者 2"),
  3= >println!("3"), _ = >println!("Any value"),}// 1 或者 2
Copy the code

Match the range

Similar to interval, you can also use.. = to match an interval:

let x = 2;
match x {
	1..=5= >println!("X >= 1 and x <= 5"), _ = >println!("Any value"),}// x >= 1 and x <= 5
Copy the code

Instead of numbers, we can use the char type to represent a character interval:

let y = 'c';
match y {
  'a'..='d'= >println!("Between a - d"),
  'e'..='h'= >println!("E - between h"), _ = >println!("Any value"),}/ / a - between d
Copy the code

Deconstructive structure

Structures can also be used in match patterns. We may not have talked about deconstructing structures in the previous section, but let’s look at the normal way of deconstructing structures:

struct Point {
  x: i32,
  y: i32,}let p = Point { x: 10, y: 15 };

// The pattern is on the left and the structure is on the right
let Point { x, y } = p;
println!("x = {} y = {}", x, y);
// x = 10 y = 15
Copy the code

You can rename a field in a structure when you deconstruct it:

let Point { x: a, y: b } = p;
println!("a = {} b = {}", a, b);
// a = 10 b = 15
Copy the code

Finally, we deconstruct it in the match condition, and the pattern is the same as above:

let p = Point { x: 0, y: 15 };
match p {
  Point { x: 0, y } => println!("On the Y-axis, y = {}", y),
  Point { x, y: 0} = >println!("On the X-axis, x = {}", x),
  Point { x, y } => println!("Neither x nor y, x = {} y = {}", x, y),
}
On the Y-axis, y = 15
Copy the code

Deconstruct the enumeration

Structs can be deconstructed, and enumerations are no exception:

enum Message {
  ChangeColor(i32, i32, i32),
}
let msg = Message::ChangeColor(0.160.255);

// Again, the left side of the equals sign is the pattern, and the right side is the enumeration value
letMessage::ChangeColor(r, g, b) = msg; println! ("r: {}, g: {}, b: {}", r,g,b);
// r: 0, g: 160, b: 255
Copy the code

To match multiple variants of enumerated values in a match statement:

enum Message {
  Quit,
  Move { x: i32, y: i32 },
  Write(String),
  ChangeColor(i32.i32.i32),}let msg = Message::ChangeColor(0.160.255);
match msg {
  Message::Quit => println!("Quit"),
  Message::Move { x, y } => println!("Move x = {} y = {}", x, y),
  Message::Write(str) = >println!("Write{}".str),
  Message::ChangeColor(r, g, b) => println!("ChangeColor r = {} g = {} b = {}", r, g, b)
}
// ChangeColor r = 0 g = 160 b = 255
Copy the code

Deconstruct nested structures and enumerations

Destructions also support multiple levels of nested destructions, such as the Color enumeration nested in the Message enumeration and then destructed in the match statement:

enum Color {
  Rgb(i32.i32.i32),
  Hsv(i32.i32.i32),}enum Message {
  Quit,
  Move { x: i32, y: i32 },
  Write(String),
  ChangeColor(Color),
}

// Message nested Color
let color = Color::Rgb(0.160.255);
let msg = Message::ChangeColor(color);

match msg {
  Message::Quit => println!("Quit"),
  Message::Move { x, y } => println!("Move x = {} y = {}", x, y),
  Message::Write(str) = >println!("Write{}".str),
	
  // Deconstruct the nested Color
  Message::ChangeColor(Color::Rgb(r, g, b)) => println!("Color r = {} g = {} b = {}", r, g, b),
  Message::ChangeColor(Color::Hsv(h, s, v)) => println!("Color h = {} s = {} v = {}", h, s, v)
}
// Color r = 0 g = 160 b = 255
Copy the code

Deconstruct nested structures and tuples

The Point structure is nested using a tuple and then deconstructed using a let:

let tuple = ((3.10), Point { x: 3, y: -10 });
let ((a, b), Point { x, y}) = tuple;
println!("a = {} b = {} x = {} y = {}", a, b, x, y);
// a = 3 b = 10 x = 3 y = -10
Copy the code

Ignore the entire value with _

We can omit some parameters or variables we don’t care about by using _ to prevent the compiler from alerting us that we already used them in the match statement:

fn foo(_ :i32, b: i32) {
  println!("b = {}", b);
}
foo(10.20);
Copy the code

_ cannot be used directly:

fn foo(_ :i32, b: i32) {
  println!("_ = {} b = {}", _, b); // An error is reported. _ is not allowed
}
foo(10.20);
Copy the code

Ignore partial values with nested _ :

let mut value = Some(5);
let new_value = Some(10);
match (value, new_value) {
  // Match both values of Some, but do not care what the exact value is
  (Some(_), Some(_)) = > {println!("Value already exists and cannot be overwritten");
  },
  // If one of them is None, update value_ => { value = new_value; }}println!("Updated value {:? }", value);
// Updated value Some(5)
Copy the code

Multiple places in a pattern use _ to ignore specific values:

let arr = [1.2.3.4.5];
match arr {
  [a, _, b, _, c] => println!("a = {} b = {} c = {}", a, b, c)
}
// a = 1 b = 3 c = 5
Copy the code

Variable names begin with _ to ignore unused warnings:

let _x = 1;
let y = 2; // warning: Unused variable y
// above y is warned, but _x is not
Copy the code

The difference between _ and _ variables is that _ variables bind variables:

let s = Some(String::from("Hello!"));
if let Some(_s) = s {
  println!("Matched the string");
}
println!("{:? }", s); // error, ownership of s has been transferred to the condition if let
Copy the code

_ does not bind variable values:

let s = Some(String::from("Hello!"));
if let Some(_) = s {
  println!("Matched the string");
}
println!("{:? }", s);
// Some("Hello!" )
Copy the code

Use.. Ignore the remaining values

_ can be ignored as a single value, but if we only care about one of the attributes in the structure, it is impossible to ignore all the other attributes with _, because there are too many, we can use… To ignore multiple values in the structure:

struct Point {
  x: i32,
  y: i32,
  z: i32,}let origin = Point { x: 0, y: 0, z: 0 };
match origin {
  // Ignore all values except xPoint { x, .. } = >println!("x = {}", x),
}
// x = 0
Copy the code

Ignore values in tuples:

let nums = (1.2.3.4.5);
match nums {
	// Take the first and last values and ignore all intermediate values(a, .. , b) =>println!("a = {} b = {}", a, b)
}
// a = 1 b = 5
Copy the code

. There can be no ambiguity:

let nums = (1.2.3.4.5);
match nums {
  // An error is reported because a is ambiguous and can be any position in a set of numbers(.. , a, ..) = >println!("a = {}", a)
}
Copy the code

Match additional conditions provided by the guard

Match Guard is an additional if condition specified after the match branch pattern that must also be met to select this branch. Match guards are used to express situations that are more complex than the pattern alone allows:

let num = Some(4);
match num {
  Num is Some and x is less than 5
  Some(x) if x < 5= >println!("小于5: {}", x),
  Some(x) => println!("{}", x),
  None= > ()}// Less than 5:4
Copy the code

Allow fetching external variables:

let x = Some(5);
let y = 10;
match x {
  Some(50) = >println!("x = 50"),
  // Get external y for comparison
  Some(n) if n == y => println!("Equal to y, n = {}", n),
	_ => println!("Default x = {:? }", x),
}
println!("Finally: x = {:? }, y = {}", x, y);
// Finally: x = Some(5), y = 10
Copy the code

Used in combination with multiple patterns:

let x = 4;
let y = false;
match x {
  / / and 2 | 3 | 4 matches in the first place, and if y in judgment
  2 | 3 | 4 if y => println!("yes"), _ = >println!("no"),}// no
Copy the code

@ binding

The at operator @ allows us to use conditional matches in structs, enumerations, and here we use @ in enumerations:

enum Message {
  Hello { id: i32}}let msg = Message::Hello { id: 10 };
match msg {
  Create an alias for the ID and add a limit between 1 and 10
  Message::Hello { id: value@ 1..=10} = >println!("Id between 1 and 10 {}", value),
  Message::Hello { id: value@ 11..=20} = >println!("Id between 11 and 20 {}", value),
  Message::Hello { id } => println!("Id not between 1 and 20 {}", id)
}
Copy the code

Use @ in a structure:

struct User {
  name: String,
  age: u8
}
let user = User { name: "xiaoming".to_string(), age: 10 };
match user {
  User { name, age: value @ 1..=15} = >println!("{} Ages between 1 and 15 {}", name, value),
  User { name, age } => println!("{} Age not between 1 and 15 {}", name, age)
}
// The age of xiaoming is between 1 and 15
Copy the code

Rust pattern matching is a very powerful tool in rust pattern matching, especially in the match statement. It is recommended that you try to write the above examples to improve your impression of rust pattern matching.

Cover: Follow Tina to draw America

Read more about the latest chapter on our official account