Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching tuples with multiple possible values

As tuple matching with ranges works, I hoped something similar would also work with alternatives:

match x {
    (1, 1) => println!("A"),
    (1, 2 ... 3) => println!("B"),   // ranges work
    (2 | 5, 4 | 6) => println!("C"), // this doesn't
    _ => println!("D")
}

Is there an elegant solution to this or does one have to either "unroll" the alternatives or resort to chained if/else if instead of pattern matching?

like image 947
Thomas W Avatar asked Aug 26 '16 06:08

Thomas W


2 Answers

Alternatives are not part of the syntax for patterns; a | b is not a pattern. Alternatives can only be used to combine patterns in a match arm (they are not available in if let and while let expressions either).

A workaround is to use guards:

match x {
    (1, 1) => println!("A"),
    (1, 2 ... 3) => println!("B"),
    (a, b) if (a == 2 || a == 5) &&
              (b == 4 || b == 6) => println!("C"),
    _ => println!("D")
}

Guards are arbitrary expressions (that must evaluate to a bool), so they can call functions.

match x {
    (1, 1) => println!("A"),
    (1, 2 ... 3) => println!("B"),
    (a, b) if [2, 5].contains(&a) &&
              [4, 6].contains(&b) => println!("C"),
    _ => println!("D")
}
like image 181
Francis Gagné Avatar answered Nov 19 '22 08:11

Francis Gagné


Since Rust 1.53, pattern matching was extended to allow nested | patterns. So the original presented example compiles as is in that regard (Playground link):

match x {
    (1, 1) => println!("A"),
    (1, 2..=3) => println!("B"),
    (2 | 5, 4 | 6) => println!("C"),
    _ => println!("D")
}

(... for inclusive ranges were deprecated in 2021 Edition in favor of ..=)

like image 5
kmdreko Avatar answered Nov 19 '22 10:11

kmdreko