match
De-Nesting
You often end up having deeply-nested code especially when match
is involved, which reduces
readability. There are a few things you can try for de-nesting.
Use if let
Source: https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html
Sometimes if let
can make your match
more readable and concise. The source above provides a good
example.
Don't write:
fn main() { let optional = Some(7); match optional { Some(i) => { println!("This is a really long string and `{:?}`", i); // ^ Needed 2 indentations just so we could destructure // `i` from the option. }, _ => {}, // ^ Required because `match` is exhaustive. Doesn't it seem // like wasted space? }; }
Do write:
fn main() { let optional = Some(7); if let Some(i) = optional { println!("This is a really long string and `{:?}`", i); } }
Use while let
Source: https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html
Similar to if let
, while let
can make your match
more readable and concise. The source above
provides a good example.
Don't write:
fn main() { let mut optional = Some(0); // Repeatedly try this test. loop { match optional { // If `optional` destructures, evaluate the block. Some(i) => { if i > 9 { println!("Greater than 9, quit!"); optional = None; } else { println!("`i` is `{:?}`. Try again.", i); optional = Some(i + 1); } // ^ Requires 3 indentations! }, // Quit the loop when the destructure fails: _ => { break; } // ^ Why should this be required? There must be a better way! } } }
Do write:
fn main() { let mut optional = Some(0); // Repeatedly try this test. while let Some(i) = optional { if i > 9 { println!("Greater than 9, quit!"); optional = None; } else { println!("`i` is `{:?}`. Try again.", i); optional = Some(i + 1); } } }
Use Tuple Matching
Source: https://github.com/ferrous-systems/elements-of-rust#tuple-matching
If you need to match on multiple variables, you can group them as a tuple and flatten the nested
match
expressions.
Don't write:
fn main() { let first_match: Option<i32> = Some(1); let second_match: Option<i32> = None; match first_match { Some(_) => { match second_match { None => println!("None found"), _ => (), } }, _ => () } }
Do write:
fn main() { let first_match: Option<i32> = Some(1); let second_match: Option<i32> = None; match (first_match, second_match) { (Some(i), None) => println!("None found"), _ => (), } }
Use match
Guards
Source: https://doc.rust-lang.org/rust-by-example/flow_control/match/guard.html
match
guards are additional conditions you can use to filter a match
arm. The following examples
are adapted from the above source.
fn main() { let pair = (2, -2); println!("Tell me about {:?}", pair); match pair { (x, y) => { if x == y { println!("These are twins"); } else if x + y == 0 { println!("Antimatter, kaboom!"); } }, (x, _) => { if x % 2 == 1 { println!("The first one is odd"); } }, _ => println!("No correlation..."), } }
The above can be revised as follows.
fn main() { let pair = (2, -2); println!("Tell me about {:?}", pair); match pair { (x, y) if x == y => println!("These are twins"), // The ^ `if condition` part is a guard (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), (x, _) if x % 2 == 1 => println!("The first one is odd"), _ => println!("No correlation..."), } }
Use match
Binding
Source: https://doc.rust-lang.org/rust-by-example/flow_control/match/binding.html
You can bind a matching value to a variable. The above source has good examples.
Don't write:
fn main() { let num = Some(42); match num { Some(n) => { if n == 42 { println!("The Answer: {}!", n); } else { println!("Not interesting... {}", n); } }, _ => (), } }
Do write:
fn main() { let num = Some(42); match num { Some(n @ 42) => println!("The Answer: {}!", n), Some(n) => println!("Not interesting... {}", n), _ => (), } }
It is even shorter than using a match
guard.
fn main() { let num = Some(42); match num { // Got `Some` variant, match if its value, bound to `n`, // is equal to 42. Some(n) if n == 42 => println!("The Answer: {}!", n), // Match any other number. Some(n) => println!("Not interesting... {}", n), // Match anything else (`None` variant). _ => (), } }
Don't write:
fn age() -> u32 { 15 } fn main() { let age = age(); match age { 0 => println!("I haven't celebrated my first birthday yet"), 1 ..= 12 => { println!("I'm a child of age {:?}", age); }, 13 ..= 19 => { println!("I'm a teen of age {:?}", age); }, // Nothing bound. Return the result. _ => println!("I'm an old person of age {:?}", age), } }
Do write:
fn age() -> u32 { 15 } fn main() { match age() { 0 => println!("I haven't celebrated my first birthday yet"), n @ 1 ..= 12 => println!("I'm a child of age {:?}", n), n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n), // Nothing bound. Return the result. n => println!("I'm an old person of age {:?}", n), } }