Syntax of block-like expressions in match arms


#1

Hi! I’ve noticed that rustc treats block-like expressions differently for determining if the ; or , is needed:

fn stmt() {
    # no need for semicolons
    { () }
    if true { () } else { () }
    loop {}
    while true {}
}

fn match_arm(n: i32) {
    match n {
        1 => { () } # can omit comma here
        2 => if true { () } else { () }, # but all other cases do need semicolons. 
        3 => loop { },
        4 => while true {},
        _ => ()
    }
}

This seems just weird to me… Is there a reason you really need , after an if in match? Perhaps this is a bug that needs to be fixed?


#2

I wrangle with this too. In my case it’s:

match n {
    1 => foo()  // not legal
    2 => foo(); // not legal either
};
{
    foo(); // fine
    foo()  // fine too
}

#3

Seems like at one point all right hand blocks/statements of match arms were meant to end with a comma to separate them from the next arms.

I think that makes more sense than extending the ability to omit the comma to other block-like right hands.


#4

I think it makes sense to make these consistent. I’ve definitely found the need for a comma after an inner match annoying (you missed this one but its the same).


#5

I came here to figure out what conflicts this would introduce from a parsing perspective and, armed with an LALR grammar I recently wrote, I tried loosening the need for a comma after block-like expressions (if, loop, for, while, match, macros with braces, and unsafe blocks) to see what conflicts that would introduce. To my surprise, it didn’t introduce any. Of course, it could be my grammar that is broken. :slight_smile:

Naturally, we still do need commas after struct literals even if they end in a block…


#6

A problematic case might be

let x: &i32 = unimplemented!();
match x {
   &1 => if true { 1 } else { 2 } &3 // is &3 a pattern or a bitwise and?
}

But looks like this is already forbidden in match arms (in blocks, &3 is treated as a separate expression statement).


#7

if true { 1 } else { 2 } &3 isn’t a block-like expression, so it isn’t even in the running. &3 cannot be anything but a pattern.

The problem you are describing is an ambiguity that one might see happening in expression statements. To deal with that Rust expects the LHS of a binary expression to not be block-like for it to be turned into a statement.

In any case, this poses no problems in match blocks.


#8

@harpocrates are you aware of https://github.com/rust-lang/rust/issues/30942? I think yours is the most complete LALR style Rust grammar so far, might be useful to advertise it on that issue :slight_smile:


#9

PR: https://github.com/rust-lang/rust/pull/40989