Or-operator in matching sub patterns

Viewed 52

The Rust documentation says not much about the |-operator for pattern matching. It only gives one example:

let x = 1;

match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}

Contrary to the answer of this question, I noticed that the following compiles:

struct S(E, usize);

#[derive(Copy, Clone)]
enum E {
    P(u8), Q(u8)
}

fn main() {
    let x = S(E::P(1), 10);
    if let S(E::P(..) | E::Q(..), _) = x {
        
    }
    
    let y = E::P(2);
    match (y, y) {
        (E::Q(..), E::P(..) | E::Q(..)) => {
            
        }
        _ => {}
    }
}

In that particular question, integers were matched, not values of enum type. Integers implement the std::ops::BitOr trait, so there would be a conflict there for sub patterns.

My questions are:

  1. Is this a feature? Why does it compile? Why does it seem to work?
  2. What are the limitations of this feature? (e.g. type must not implement BitOr or similar)
  3. What is the feature called? What is the AST node used to implement the feature called? Even the name of the type of the AST node in the compiler implementation would be a better name to use in discussion than calling it the "|-operator", which is very vague.
1 Answers

Is this a feature? Why does it compile? Why does it seem to work?

Before Rust 1.53, |s were only allowed at the top-level of a match arm, but since then they were expanded to be allowed in nested patterns. So what you see is working as intended; just the linked Q&A was out-dated.


What are the limitations of this feature? (e.g. type must not implement BitOr or similar)

On the contrary, this does not use the BitOr operator and thus will work on any type that can be matched to multiple values. Remember pattern matching employs structural equality, not value equality. And because of that, the pattern 1 | 2 will match either 1 or 2, while if it used BitOr then it'd only match 3.

Feature-wise, this is fairly unlimited:

Or-patterns are patterns that match on one of two or more sub-patterns (e.g. A | B | C). They can nest arbitrarily. Syntactically, or-patterns are allowed in any of the places where other patterns are allowed, with the exceptions of let-bindings and function and closure arguments. - Rust Reference


What is the feature called? What is the AST node used to implement the feature called? Even the name of the type of the AST node in the compiler implementation would be a better name to use in discussion than calling it the "|-operator", which is very vague.

The original RFC 2535 was simply called "or-patterns" and that is reflected in the reference documentation. Not sure about the name of the AST node, but the Pattern grammar in the Rust Reference simply has the | chain as directly part of the Pattern production and other types of patterns are deferred to PatternNoTopAlt or PatternWithoutRange.

Related