- Feature Name:
match_colon_syntax
- Start Date: 2019-06-22
- RFC PR: rust-lang/rfcs#0000
- Rust Issue: rust-lang/rust#0000
Summary
This RFC proposes adding alternative syntax for non-exhaustive branching based
on pattern matching: the match :
and match while
syntax.
If the syntax is added,
match <expr> : <pattern> => {
// statements
}
and
match <expr1> : <pattern> => <expr2>
would respectively be equivalent to current
if let <pattern> = <expr> {
// statements
}
and
if let <pattern> = <expr1> {
<expr2>
}
. The match while
syntax will have a similar equivalence to the current
while let
syntax.
Motivation
The current if let
and while let
syntax makes it easy for programmers to
confuse let
statements as expressions (with a following ;
) that return
bool
values. Let me call a let
statement with its ;
stripped a "let
clause".
Since Rust is an expression-oriented language, when one is not sure about a
langauge element, they will assume that it is an expression. The let
clause in
Rust is not an expression. This creates an exception and causes some
inconsistency and inconvenience, but the problem is not too huge. Just as this
RFC
mentioned, one will very quickly learn that this syntax does not create an
expression by some simple experiments. However, the if let
and while let
syntax makes the problem worse. In if let
and while let
syntax, the usage of
the let
clause is exactly the same as if the let
clause creates an
expression that returns a bool
value. In other words, currently, a learner who
is not sure about whether the let
clause creates an expression will be given
both clues implying that it does and clues implying that it does not.
Programmers will sometimes get confused and try to use let
clauses as bool
expressions.
In this
RFC,
it is given as a support material that programmers try to chain let
clauses
with &&
. This is a sign of confusing let
clauses as expressions. It is also
mentioned in this RFC that using ||
with let
clauses has been attempted and
allowing it will be considered in the future. In this
RFC, it is proposed that using
!
to negate let
clauses should be supported. I think these are also proof
that programmers are confusing let
clauses as expressions.
The RFC proposing &&
chain with let
clauses is accepted. Once it is
implemented and and the new version is published, the let
syntax would be even
more similar to a bool
expression. The !let
and "let
with ||
" proposals
will create even more cases where let
clauses are used as an expression. This
trend will possibly cause a result that beginners will need to memorize special
cases. They will need a list or a cheatsheet to help them to know whether a
let
clause can be used as a bool
expression in a certain case.
I feel this trend is dangerous, so I want to propose new syntax which does
not use let
to possibly stop this trend, or at least to give people who do
not want deal with the confusion an alternative.
Guide-level explanation
match <expr> : <pattern> => {
// statements
}
and
match <expr> : <pattern> => <expr2>
would respectively be equivalent to current
if let <pattern> = <expr> {
// statements
}
and
if let <pattern> = <expr> {
<expr2>
}
. The match while
syntax will have a similar equivalence to the current
while let
syntax.
Of course, just as the original match
syntax, you can use |
to match
multiple patterns:
match <expr> : <pattern1> | <pattern2> => {
// statements
}
If you want to chain multiple pattern matching conditions, you can write
match <expr1> : <pattern1> =>
match <expr2> : <pattern2> | <pattern3> =>
match <expr3> : <pattern4> => {
// statements
}
To integrate boolean conditions and adding boolean guards into it:
match <expr1> : <pattern1> =>
match <expr2> : <pattern2> | <pattern3> =>
match <expr3> : <pattern4> if flag0 == true | <pattern5> =>
if a == 4 || b <= 3 && flag1 == true {
// statements
}
This syntax does not have else
, but when you try to write
if let <pattern> = <expr> {
// some statements
} else {
// more statements
}
, you can always write
match <expr> {
<pattern> => {
// some statements
},
_ => {
// more statements
}
}
Reference-level explanation
We replace the following production:
expr : literal | path | tuple_expr | unit_expr | struct_expr
| block_expr | method_call_expr | field_expr | array_expr
| idx_expr | range_expr | unop_expr | binop_expr
| paren_expr | call_expr | lambda_expr | while_expr
| loop_expr | break_expr | continue_expr | for_expr
| if_expr | match_expr | if_let_expr | while_let_expr
| return_expr ;
with:
expr : literal | path | tuple_expr | unit_expr | struct_expr
| block_expr | method_call_expr | field_expr | array_expr
| idx_expr | range_expr | unop_expr | binop_expr
| paren_expr | call_expr | lambda_expr | while_expr
| loop_expr | break_expr | continue_expr | for_expr
| if_expr | match_expr | match_colon_expr | match_while_expr
| if_let_expr | while_let_expr | return_expr ;
match_colon_expr : "match" no_struct_literal_expr ":" match_colon_arm ;
match_colon_arm : attribute * match_pat "=>" [ expr | '{' block '}' ] ;
match_pat : pat [ '|' pat ] * [ "if" expr ] : ;
Note that match_pat
is the same as the original one used with match_expr
.
Drawbacks
Redundant: this syntax can be considered as a syntax sugar for if let
, which
is already a syntax sugar for match
with only one meaningful branch…
Limited: This syntax does not have equivalence to if let PATTERN = EXPR { } else { }
.
This RFC creates a possible pitfall:
This
RFC
introduces while let &&
syntax:
while let <pattern1> = <expr1>
&& let <pattern2> = <expr2>
&& let <pattern3> = <expr3> {
// statements
}
. The code above is NOT equivalent to
match <expr1> while <pattern1> =>
match <expr2> while <pattern2> =>
match <expr3> while <pattern3> => {
// statements
}
The former is one single loop with multiple conditions, while the later one is
multiple nested loops, each having one condition. An equivalence using the
match while
syntax would be
loop {
match <expr1> : <pattern1> =>
match <expr2> : <pattern2> =>
match <expr3> : <pattern3> => {
// statements
continue;
}
break;
}
which is sort of awkward, but I wonder how popular this use case is.
The problem of duplicate code in else
clauses cannot be solved by this syntax.
Also, generally the match
syntax is a little more verbose than the if let
syntax.
Another issue is, :
does not align well with while
.
Rationale and alternatives
Why is this design the best in the space of possible designs:
This design uses one single language element (the match :
syntax) to support
- Simple non-exhaustive pattern matching conditional execution.
- Non-exhaustive pattern matching branching with multiple conditions that should all be satisfied (the “and” case).
2 is proposed and accepted here.
Also, it is totally consistent with the current match
syntax. More importantly,
unlike if let
and while let
, it does (should) not cause any confusions.
This introduction of this syntax will also enable a strong binding between
keywords and their jobs. If a programmer completely uses match on
to replace
if let
, then when reading their code, whenever one sees match
, they know
they have a branching here, and when one sees let
, they know here is a
(simple) declaration.
We can also possibly extend this syntax to support matching negation which is proposed here. The method is in the future possibilities section.
What other designs have been considered and what is the rationale for not choosing them:
The usage of :
can certainly be discussed more. I am using it here because
if
cannot be used as it is already used for guards, and using :
does not
need any additional keywords. If people do not like it, it can certainly be
changed into other things, like @
, on
, only
, nex
(for non-exhaustive),
etc.
This RFC’s rationale section provides very good insights on other possible designs. I agree with all of them except for
- I do think causing programmers to confuse
let
clauses as expressions is a big problem. - I do think current the
match
syntax places patterns at lhs and expressions at rhs, so it is acceptable to have a syntax which starts withmatch
that has such a lhs/rhs configuration.
What is the impact of not doing this:
Just as mentioned in the Motivation section, we are having a dangerous trend
of making let
clauses bool
expressions in some cases but no other cases.
This will create pitfalls and difficulties in memorization for future Rust
learners. For a language that has the potential to replace C++, I really hope
this will not happen. Also, I do not think it a good idea to just make the
clauses bool
expressions.
Prior art
Swift
Swift uses let
for all pattern matching. I feel it is where Rust developer got
the inspiration of if let
and while let
. However, what is good for Swift
might be bad for Rust. Swift is not an expression-oriented language. One can
only blame themselves if they assumed anything in Swift is an expression, so
have such syntax will not cause as strong a confusion as what the syntax can
cause in Rust. Also, Swift uses let
anywhere there is a pattern matching. This
is not the case in Rust. We also have match
in Rust which is specialized in
branching (for comparison, let
statements in Rust are specialized for
declaration). I think it makes more sense to make all syntax used for branching
similar to each other.
Unresolved questions
What parts of the design do you expect to resolve through the RFC process before this gets merged:
The symbol/keyword separating the pattern and expression can be further discussed.
What parts of the design do you expect to resolve through the implementation of this feature before stabilization:
What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC:
Should we make let <pattern> = <expr>
clauses expressions: I think for
consistency we should, but it should not return a bool
. That would be
confusing. I think it can either return <expr>
like C/C++ or simply return
()
.
Future possibilities
Negation
We can extend this syntax to support matching negation proposed here. The syntax would be like this:
match <expr> : not <pattern> => {
}
Deprecation
Personally I hope if let
and while let
would get deprecated. I do not think
that can happen, though.