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),
    }
}