Extending the ability to elide commas from match arms


#1

Currently, one can omit the comma at the end of a match arm if the RHS of the => is a bare block expression:

match foo {
    Foo => { ... } // no comma!
    Bar => { ... }
}

I’ve often wanted to modify this rule to make it more consistent with how expressions and statements work. The motivation is that there are certain types of expressions which when used in statement position do not require semicolons for separation. For example, the following:

if foo { bar; };
loop { baz(); };
{ qux(); };
match something { ... };

can be rewritten more idiomatically as:

if foo { bar; }
loop { baz(); }
{ qux(); }
match something { ... }

This ability to remove semicolons does not apply to all expressions—only certain ones that end in } (if/else, while, loop, for, match, bare blocks, macros invoked like foo! { ... }, and probably some other things I’ve forgotten). The change I’d like to make to match arms would be to extend this rule to comma elision. So under this change, the following would be valid:

match foo {
    Foo => if foo {
        bar
    } else {
        baz
    }

    Bar => loop {
        baz();
    }

    Baz => {
        qux();
    }

    Qux => match something {
        ...
    }
}

Unfortunately, this is a breaking change. Edit: turns out it’s actually not a breaking change! See my comment below. The rest of the comment is left here for posterity. However, it’s a relatively minor one—the only code that would be broken by a careful implementation would be code similar to the following:

match foo {
    Foo => if foo { some_function } else { some_other_function }(),
    Bar => if foo { some_array } else { some_other_array }[],
    Baz => if foo { some_int } else { some_other_int } & 1,
    Qux => if foo { some_bool } else { some_other_bool } && some_predicate(),
}

where a block-like expression immediately following the => of a match arm is followed by a token that can also start a pattern. As far as I know, the only such tokens are &, &&, (, and [. From this I would estimate that virtually no real code would be broken by this change, and any code that would get broken could easily be fixed in a manner that would let it continue to compile on older versions of Rust.


#2

I wouldn’t mind the change, but I don’t know if it’s a big enough improvement to warrant a breaking change.

I would consider it better style to wrap complex expressions in their own curly braced block anyway.

match foo {
    Foo => {
        if foo {
            bar
        } else {
           baz
       }
    }
    _ => ...
}

#3

While digging through the parser to see if I could potentially implement this, I discovered that match arm RHSs are already treated during parsing similarly to statements, meaning that this wouldn’t be a breaking change at all! (On reflection, I probably should have tried compiling my own example from above that was meant to demonstrate code that worked today. :stuck_out_tongue:)

Edit: Welp, it actually totally is a breaking change, but a super(-duper) minor one. The only code that would be broken would be code like this:

match foo {
    Foo => mac! { ... }(),
    Bar => mac! { ... }[0],
    Baz => mac! { ... } & 1,
    Qux => mac! { ... } && some_predicate(),
}

I actually tried compiling this time, and it totally parses, but wouldn’t under this change. I would bet a large sum of play money that this would break literally no Rust code anywhere—in the Rust compiler itself, the sequence => <word>! { only appears three times, none of which are terminated by }(...), }[...], } & ..., or } && ....