Rust 1.42.0 introduces the matches! macro to boolean test if a pattern matches an expression. This is a neat little macro. I find it useful when I want to use an if-let statement without actually binding anything. I personally have been using if-let statements for speed since I discovered this a couple months ago.
This Pre-RFC is to propose a syntax for matches! that would work as a drop-in replacement for == in cases where the left side expression matches the right side pattern.
Pros:
Encourages the use of the matches! function, leading to new design patterns and a faster idiomatic Rust
Cons:
Encourages the use of the matches! function, leading to preferences to a Rust-only syntax, similar to Javascript's === vs ==. There should instead be efforts in the compiler team to internally attempt to rewrite pre-existing == statements before defaulting to PartialEq.
Options for this syntax include: =>: This borrows syntax from match (although in match it symbolizes destructuring). Would cause confusion in order of operations. =~: The match operator from Perl. ~: Similar to the match operator from Perl but with requisites for macro_rules. matches: Create a new keyword that can only be used inline in if statements. is: See my complete and utter disdain for this syntax here
Further Arguments
I would like this to include if-let bindings
I don't agree. I use the matches! syntax to fix this uncommon yoda case:
let x = 42;
if let 42 = x { /* stuff */ }
and adding bindings to matches! will make the common case yoda-like.
This was a placeholder for me while I still formulate my ideas, I have edited for clarity.
I'm imagining a situation similar to javascript's === vs ==, although the reasoning would be performance rather than correctness. Look at the following example:
Another syntax that has been suggested before is expr is pat:
if src() is Ok(0) {
/* stuff */
}
I find this more natural to read than matches, if let or a new sigil like ~=. I think it could even be added as a context-sensitive keyword, so it doesn't require a new edition.
However, I think this would only be worth it, if it would allow bindings, e.g.
if something() is Ok(n) && n > 4 && something_else(n) is Some(val) {
println!("{}", val);
}
Because then if let could be deprecated in favor of is, which is more powerful, more general and more readable.
The problem with is is that it's too general. If someone sees a new sigil then they know "ok this has a specific definition to Rust". is implies being. And has a lot of locality context. English speakers will lump more meaning onto it than non-native speakers and not on purpose, just through constant overuse.
IIRC the only language to get is right is Python because it compares the underlying pointers. If you really want to give Rust an is operator then it would need to be used for this case and this case only.
However, I think this would only be worth it, if it would allow bindings [...]
My RFC contains the line "I find it useful when I want to use an if-let statement without actually binding anything" as a direct argument for using matches! when you don't want to contend with if-let yoda syntax:
let x = 42;
if let 42 = x {
/* do something */
}
I prepared an argument to defend separating the syntaxes :
Unfortunately it derives that using matches! for binding will lead to an uno reverse-reverse and not only cause yoda syntax, but cause yoda syntax in the much more common case
However I think arrow syntax actually makes this work
let myvar = Some(42);
if myvar => Some(x) {
/* do something with x */
}
I am not a compiler guru, is there any way to get a second opinion?
It seems odd that rules inside a match block also apply to statements outside of a match block but I understand that software architecture is never perfect and issues like this crop up from time to time. I'd like more detail on the problem and how easy it would be to work around but I also don't want to steal anyone's time if they do not wish to be invested in this argument.
this has nothing to do with software. It is about syntax.
Not necessarily possible, at least not easily. The => from match is not just a binary operator, so this is not your usual operator expression parsing anymore. To make it even more complicated, the match-arm-=> is either PAT if EXPR => EXPR or PAT => EXPR while the matches-=> would be EXPR => PAT. Total madness to apply any kind of associativity.
Approaching a if b => c => d you would at least need to try parsing c => d as an expression which can either fail (when c is only valid syntax for a pattern) or succeed but then be immediately discarded because you actually need to parse it as a pattern followed by => instead (and make sure to discard it quickly, crucially before fully parsing d, otherwise you’re quickly in quadratic complexity land). Also imagine the inconsistency if the match arm a if b => c => d is supposed to mean a if (b => c) => d but the match arm a => c => d probably still means a => (c => d) (since the left hand side of the arm can only be a pattern).
And taking any anology to == would suggest to have no associativity but require parentheses anyways.
(Disclaimer: speaking as a member of the grammar wg but not on behalf of the group)
Adding new sigils is highly problematic for macro_rules! macros. E.g. >>= gets handled as a single token for macro_rules!, and must be matched as >>= and not as > > = or >> = or anything else. ~= is currently interpreted as ~ =, and the two literal sequences in source code are completely interchangeable in both macro definitions and use. Adding a new token to glue the two together would potentially be possible, but difficult and with surprising edge cases, in order to avoid breaking macro_rules! macros.
Only parsing the ~= token in a new edition mode would change the trade-off to be a decent amount less problematic, but it'd still be a surprisingly subtle edition difference and make calling macros cross-edition using ~= weird 202X->2018 (have to break it into ~ =) and impossible 2019->202X (as there is no way for edition 2018 to write the token if it's edition gated).
(Disclaimer²: I am not providing an opinion on new syntax for matching at this time.)
Actually I think I understand. Any extra sigils would break custom syntax in somebody's macro_rules. I hate to bring up the try operator but I'm curious what path they took to avoid this.
? wasn't really problematic because it's a single character and wasn't already part of a multi-character operator. Multi-character operators are the challenge for the reasons mentioned above.
It seems like this issue interferes with a huge number of proposals for new syntax. Which in most cases is not much of a problem, since most proposals for new syntax are unjustified for other reasons. (I'm not a fan of this one.) Indeed, it's plausible that a need will never arise to add new sigils to Rust. But if one ever does, it will be too bad if this gets in the way.
Surely there's some kind of solution. Using >>= as an example, what if it were parsed as two tokens, but with a special case to make it an error if the tokens were not physically adjacent in the source file? Yes, there are edge cases (proc macros), and yes, it's a hack, but it should be doable. And Rust already has at least one hacky edge case around sigil tokenization: the fact that >> is sometimes treated as two closing brackets, despite being a single token. C++ used to force you to write > > in that situation, but people realized that it makes no sense when there's no real ambiguity in the syntax; it was fixed in C++11, and from the beginning in Rust. A similar principle should apply here.