It's certainly cleaner, but my immediate thought when reading was "that's a compile error" because it looks like it's declaring a variable with a type mismatch. This is where, in other languages, the "case" keyword (or similar) comes in handy (eg: Dart's if-case syntax[1]), except Rust doesn't have that because of its headlong pursuit of 'conciseness'.
I think the fact you had to re-consider it shows you're not thinking in patterns. Rust is a language which always had pattern matching so in Rust it feels natural to, for example:
while let Some(work) = inbox.pop() { /* ... */ }
[edited: thanks HN, of course since this is code I don't need to escape the asterisks, unless I do]
It's not that... well, it might be, but I use pattern matching fairly regularly in a number of languages. But Rust has hijacked the variable syntax and changed it beyond recognition: there's no matching operator being used; the same code does different things based on its surroundings:
// This causes a compilation error
let Some(work) = inbox.pop()
// But putting it 1:1 within a while statement is fine?
while let Some(work) = inbox.pop() { /* ... */ }
Whereas, with Java's pattern matching:
while (inbox.poll() instanceof final Work work) { /* ... */ }
It borrows the variable declaration syntax too, which can be extracted fine without issue, but there's an operator being used to express that pattern matching is happening.
Different languages have different uses, histories, and quirks, yes, and this is by no means to suggest that Java is a better language or that it doesn't have its own flaws. But Rust coopting of that syntax makes the language harder to learn and harder to comprehend at a glance, in my opinion. It seems to be like one of those situations where, once you learn it, it's fine, but that's my point.
First of all, you're right, it does indeed do different things. The stand-alone variant only accepts irrefutable patterns, hence why it needs an "else" branch.
But just for fun, let's make it compile!
enum Result<T> {
Some(T),
}
use Result::Some;
fn pop() -> Result<u32> {
Some(42)
}
fn main() {
// This doesn't cause a compilation error
let Some(work) = pop();
}
I will argue that it's not different, it's exactly the same thing. In both cases you are matching a pattern. The only difference is that in `if` and `while`, because they are conditional, the pattern is allowed to not match (refutable), while in bare `let` it must match (irrefutable).
I'll assume you know you missed a semi-colon, so we'll fix that, which still gets us a compiler error, but specifically the diagnostic says: pattern `None` not covered and it suggests:
let Some(work) = index.pop() else { todo!() };
You seemed puzzled by the fact we can use this pattern for let while, but of course when our pattern doesn't match (for None) the loop ends, that's what a while-let loop does, the if expression which may not match needs a clause for the case where it doesn't match, the suggestion is an else clause.
Remember Rust is a statically typed expression language so Python type situations where maybe the pattern matched or maybe it didn't and something will happen but the language doesn't promise what, those aren't OK because we have static typing, what is the type of "Eh, I don't know, whatever" ?
Edited:: Aha, I realised you wrote "1:1 within a while statement" and now I think I see the problem. That's not a while statement, Rust doesn't have those - it does have while loop expressions - but this isn't one of those either, this is while let, it's different.
This isn't a while loop where the while condition happens to be
a variable assignment, Rust doesn't have that. There's a reason while let has a whole separate entry in the book. This is syntax for a loop which repeatedly performs a pattern match and always exits when it fails.
This is honestly a pretty perfect example of why criticising programming languages can be so frustrating: people are completely unwilling to meet you where you're at. You're response here is to say that of course while-let does that, because that's what while-let does, that's how while-let looks. I'm not being critical of Rust having pattern matching as the while-loop condition, but of how this is expressed in code, specifically with how it looks exactly like an ordinary variable definition, but doesn't at all function like one, and how that can be confusing to a non-zero amount of people. That's the extent of my complaint. I'm ganna go now.
I think that's one of the things I struggle with in Rust; pattern matching is integral and I'm not thinking in patterns (yet). When I see "while let" I don't think about patterns at all, my brain goes straight to variable assignment.
- [1] https://github.com/dart-lang/language/issues/2181