Match guard in multiple pattern

Any guard on multiple patterns will apply to all the patterns rather than just the one it is in, for example:

let c = '.';
match c {
    '.' | _ if c.is_ascii_whitespace() => println!("'.' or whitespace"),
    _ => (),
}

Rust playground

will not print anything since the if guard will apply to '.' too. Is there anyway to achieve the behaviour intended that won't involve a separate match arm?

The best I could come up with would be to move the . comparison into the if-clause:

fn main() {
    let c = '.';
    match c {
        _ if c.is_ascii_whitespace() || c == '.' => println!("'.' or whitespace"),
        _ => (),
    }
}

Playground.

Yes, that's the solution that I have been using, and honestly just curious if there's a better solution or not.

I don't think the syntax gives you any other choice tbh. As you rightfully deduced, the match guards apply to the whole pattern and the match arm is only executed when the match guard returns true:

When the pattern matches successfully, the pattern guard expression is executed. If the expression evaluates to true, the pattern is successfully matched against.


An alternative to using a match guard in the first place would be to look at the implementation of char::is_ascii_whitespace and use the pattern it uses as part of your own pattern:

fn main() {
    let c = '.';
    match c {
        '.' | '\t' | '\n' | '\x0C' | '\r' | ' ' => println!("'.' or whitespace"),
        _ => (),
    }
}

Playground.

2 Likes

That is the other option indeed. Thanks for the suggestions!

1 Like
let c = '.';
match (c, c.is_ascii_whitespace()) {
    ('.', _) | (_, true) => println!("'.' or whitespace"),
    _ => (),
}

That's also a good solution, but unfortunately, this is a bit too clunky when there are other match arms that don't care about is_ascii_whitespace, in my case, about 6 or 7 other ones.

A little bit of duplication makes this cleaner:

let c = '.';
match c {
    '.' => println!("'.' or whitespace"),
    _ if c.is_ascii_whitespace() => println!("'.' or whitespace"),
    _ => (),
}

And if you have a long block in match arm, factor it in a function.

1 Like