Thomas W
Thomas W

Reputation: 15371

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?

Upvotes: 20

Views: 20658

Answers (2)

kmdreko
kmdreko

Reputation: 60022

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 ..=)

Upvotes: 17

Francis Gagné
Francis Gagné

Reputation: 65742

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

Upvotes: 25

Related Questions