What's the trick of this example?

fn main() {
    let x = |_| Some(1);
    let (|x| x) = match x(..) {
        |_| Some(2) => |_| Some(3),
        |_| _ => unreachable!(),
    };
    assert!(matches!(x(..), |_| Some(4)));
}

This example is just compiled. Take a first glance at this example, you might be surprised why the closure can be a pattern.

The magic is |x| x, |_| Some(2), |_| _ , and |_| Some(4) is not a closure. They are patterns. So, why is the prefix | permitted in a pattern? Is |a|b syntactically equivalent to a | b? If it is, where is the rule?

1 Like

heya :wave:, the leading pipe for a match arm was introduced in Rust 1.25:

You can now have | at the start of a match arm.

Though rustfmt defaults to remove it (which I think fits most people's intuitive preference)


Tangentially there was once when many usernames on the rust community discord were random patterns that compiled (usually to ())

8 Likes

So, what is the meaning of permitting | at the start of a pattern? Doesn't it raise the problem that that pattern is ambiguous with closure?

No, because a closure is not a pattern.

All of Rust's syntax is fully specified in the Reference.

Reading through the RFC, it's mostly intended to alleviate the fencepost problem for macros: Without this, either the first or last item in the alternation has to be special-cased.

Secondarily, other languages (notably F#) allow this syntax in their match expressions and some programmers with that background use it habitually to improve their code's formatting.

2 Likes

I didn't say the closure is a pattern. I said that increasing the pattern that has the prefix | could bring confusion for users since they look the same.

Yes, it could. That's too bad. You need to format your code in a way that it doesn't.

For confirming, Is |a | c( a pattern) syntactically equivalent to a | c in any aspect?

Yes.

It allows you to write complex match expressions in this syntactically regular form:

match foo {
    | Bar(x, y, z)
    | Baz { one, two, .. }
    | Bucks => { /* stuff */ }
    
    ...
}

Just as with trailing comma, it makes it easier to reorder the cases and they format in a prettier way.

1 Like

Isn't it the same easy to read if we write them in the following form?

match foo {
    Bar(x, y, z) |
    Baz { one, two, .. } |
    Bucks => { /* stuff */ } |
    ...
}

I don't see the benefit of the prefix | in this example.

That's a matter of personal preference. But note that trailing | is not allowed in patterns, so you can't easily interchange the lines with patterns, and the last pattern in a sequence won't have either leading or trailing |. Personally I prefer the leading |, because it immediately separates single-pattern cases from multi-pattern ones.

Remember that the compiler can be a big help for silliness like this:

warning: unreachable pattern
 --> src/main.rs:4:13
  |
4 |         |_| Some(2) => |_| Some(3),
  |             ^^^^^^^
  |
  = note: `#[warn(unreachable_patterns)]` on by default

warning: unreachable pattern
 --> src/main.rs:5:9
  |
5 |         |_| _ => unreachable!(),
  |         ^^^^^

warning: unreachable pattern
 --> src/main.rs:7:33
  |
7 |     assert!(matches!(x(..), |_| Some(4)));
  |                                 ^^^^^^^

Thus the right solution for anyone writing code like that is to ban them from your repo :upside_down_face:

8 Likes

So, I ask why we try to introduce the syntax into the current edition. It appears to me that it can easily produce ambiguity between the pattern and the closure.

Sure, but only when someone is actively trying to be malicious and you're not reading warnings and you're not using rustfmt. Rust doesn't prevent bad code.

There's a bunch of pattern cases that are imperfect without the warnings. match foo.cmp(&bar) { Less => … } is a classic, for example. But that's why warnings exist.

1 Like

Current edition? It was stabilized in 1.25 (before editions / Edition 2015), like @azriel91 said.

If you read the related issues, it certainly wasn't universally popular, and the OP demonstrates some confusing-to-humans constructions. But there's no parsing ambiguity; no pattern can start with |, and the leading | is just swallowed, as far as I see. Closures aren't patterns, like @H2CO3 said.

You can create obfuscated or confusing code in all sort of other ways too. Even code that looks normal! I don't know that I've used a language where that isn't the case.

This particular ship sailed over 4 years ago.

5 Likes

Isn't that

| _ |  Some(1) => println!("0"); 

a valid pattern? where the pattern is starting with |

No atomic pattern, ie. a leading | always, unambiguously means alternation.

No atomic pattern

The entire | _ | is a pattern, I think.

It's not:

fn main() {
    match () {
        | _ | => {}
    }
}
error: a trailing `|` is not allowed in an or-pattern
 --> src/main.rs:3:13
  |
3 |         | _ | => {}
  |         -   ^ help: remove the `|`
  |         |
  |         while parsing this or-pattern starting here