Patterns
Patterns are used to match values against structures and to, optionally, bind variables to values inside these structures. They are also used in variable declarations and parameters for functions and closures. -- Ref
Refutability
A pattern is said to be refutable when it has the possibility of not being matched by the value it is being matched against. Irrefutable patterns, on the other hand, always match the value they are being matched against. Examples:
#![allow(unused)] fn main() { let (x, y) = (1, 2); // "(x, y)" is an irrefutable pattern if let (a, 3) = (1, 2) { // "(a, 3)" is refutable, and will not match panic!("Shouldn't reach here"); } else if let (a, 4) = (3, 4) { // "(a, 4)" is refutable, and will match println!("Matched ({}, 4)", a); } }
Binding modes
To service better ergonomics, patterns operate in different binding modes in order to make it easier to bind references to values. When a reference value is matched by a non-reference pattern, it will be automatically treated as a ref or ref mut binding. Example:
#![allow(unused)] fn main() { let x: &Option<i32> = &Some(3); if let Some(y) = x { // y was converted to `ref y` and its type is &i32 } }
If a binding pattern does not explicitly have ref, ref mut, or mut, then it uses the default binding mode to determine how the variable is bound. The default binding mode starts in "move" mode which uses move semantics.
Patterns
fn main() { // 1. Wildcard Pattern // _ (an underscore symbol) is used for wildcard pattern, and it matches any value let (a, _) = (1, 2); assert_eq!(a, 1); match a { _ => println!("a can be any number"), } // 2. Literal Pattern : integer literal for i in -3..2 { match i { -3 | -2 | -1 => println!("Negative Number"), 0 => println!("Zero"), _ => println!("Positive Number"), } } // Literal Pattern: char literal for a in 'a'..'z' { match a { 'a' | 'e' | 'i' | 'o' | 'u' => println!("It's vowel"), _ => println!("It's consonant"), } } // 3. Range Pattern let ch = 'a'; match ch { 'a'..='z' => println!("lowercase letter"), 'A'..='Z' => println!("uppercase letter"), _ => println!("other"), } // 4. Identifier Pattern let x = 16; match x { // bind x to e, print e if it is between 1 and 10 inclusively e @ 1..=10 => println!("{e}"), // bind x to e, print e if it is greater than 10 e @ 11.. => println!("{e} is greater than 10."), e => println!("{e}"), } // this is also an identifier pattern, bind "hello" to s1 let s1 = "hello"; println!("{s1}"); // same as above, bind "hello" to s using match match "hello" { s => println!("{s}"), } // Identifier Pattern with ref or mut let z = "Zan".to_string(); match z { e => println!("{e}"), } // println!("{z}"); // compile time error cause z is moved to e and no longer valid here // with ref let z = "Zan".to_string(); match z { ref e => println!("{e}"), } assert_eq!(z, "Zan"); // with ref mut let mut z = "Zan".to_string(); match z { ref mut e => *e = "Ryan Zan".to_string(), } assert_eq!(z, "Ryan Zan"); // 5. Slice Pattern for fixed-sized array let odds = [1, 2, 3, 4, 5]; match odds { [1, 2, a, _, _] => { println!("Starts with 1 and 2"); println!("The third is {a}") } _ => println!("Starts with something else"), } // Slice Pattern for slices of dynamic size let evens = vec![2, 4, 6]; match evens[..] { [a, b] => println!("{a}, {b}"), [a, b, c] => println!("{a}, {b}, {c}"), _ => println!("Not Matched"), } // 6. Tuple Pattern let (i, s) = (7, "monkeys"); assert_eq!(i, 7); assert_eq!(s, "monkeys"); // 7. Tuple Struct Pattern struct Point(f32, f32); let p = Point(1.1, 2.2); match p { Point(x, y) => println!("{x}, {y}"), } // 8. Rest Pattern - only be used in tuple, tuple struct, and slice pattern let odds = (1, 3, 5, 7, 9, 11); let (fst, ..) = odds; assert_eq!(fst, 1); let (.., last) = odds; assert_eq!(last, 11); let evens = vec![2, 4, 6, 8]; fn sum(slice: &[i32], acc: i32) -> i32 { match slice { [] => acc, [h, tail @ ..] => sum(&tail, h + acc), } } let r = sum(&evens, 0); assert_eq!(r, evens.iter().sum()); // 9. Reference Pattern let one = &1; match one { &1 => println!("yes"), _ => println!("hmm"), } let maybe: &Option<i32> = &Some(10); match maybe { &Some(x) => println!("{x}"), &None => println!("Nothing"), } // or simply like below (check binding modes section) let some_val = &Some(10); match some_val { Some(x) => println!("{x}"), None => println!("None"), } // 10. Grouped Pattern let two = &2; match two { &(1..=5) => println!("Between 1 and 5"), &(6..=10) => println!("Between 6 and 10"), _ => println!("Other"), } // 11. Path Pattern with enum enum Color { Red, Green, Blue, } let red = Color::Red; match red { Color::Red => println!("red"), Color::Green => println!("green"), Color::Blue => println!("blue"), } // Path Pattern with constant const MAX_VALUE: i32 = 100; let x = 75; match x { MAX_VALUE => println!("The value is the maximum"), _ => println!("The value is something else"), } // 12. Struct Pattern struct Location { x: i32, y: i32, } let p1 = Location { x: 1, y: 2 }; match p1 { Location { x: 1, y: 3 } => println!("A"), Location { x: 2, y: 2 } => println!("B"), Location { .. } => println!("Unknown"), } // 13. Macro Invocation Pattern let _v = vec![1,2]; // this is macro invocation patter - invoke vec macro // another simple example macro_rules! foo { ($x:expr) => { $x }; } let x = 5; match x { foo!(5) => println!("It's 5"), _ => println!("The value is something else"), } }