Suppose we require a keyword – say var – before all binding patterns. This results in having to write things like for (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {},
which is quite a bit more verbose than the current for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}.
Not to mention you’ll have to write letvar x = 0; just to declare a variable, unless you redesign the language to allow you to just write var x = 0 (and if you do that, you’ll also have to somehow support a coherent way to express ifletSome(x) = arr.pop() {} and letSome(x) = arr.pop() else {todo!()}).
Suppose we require a keyword – say const – before all value-matching patterns that look like variables. Then, what’s currently
and you always run the risk of forgetting a const and accidentally binding a new match-all variable named None – the main footgun that syntactically distinguishing binding and value-matching patterns was meant to avoid in the first place.
Suppose we require a sigil such as $ before one type of pattern. Probably the best solution in my opinion, but that’s one symbol that can no longer be used for other things in a pattern context. Also, if you’re already using sigils before variable names for other purposes (I’ve been sketching out a language where a pointer variable $x can be auto-dereferenced by writing x), doubling up is really unpleasant.
…So I can understand why Rust chose to give the same, most concise possible syntax for both binding and value-matching patterns. At least compiler warnings (unused, non-snake-case variables) are there to provide some protection from accidentally turning one into the other.
I went the “only let introduces bindings” route, and I’m pretty happy so far:
if (left.next(), right.next())
...is (Some(let l), Some(let r)) { /* use l and r */ }...is (Some(let l), None ) { /* use l */ }...is (None, Some(let r)) { /* use r */ }...is (None, None ) { /* use nothing */ }
}
Or you could put the let before the Some(...) as let Some(l), which allows us to keep the current ifletSome(...) = ... syntax. Either of those would feel more consistent than the current implementation.
I completely forgot that unit structs/variants define their own associated consts. I wonder if in patterns the type can be used instead of the associated const though? That might resolve a lot of the headache. It’d mean changing the way the ident is resolved to looking in the type namespace though.
const <block> already works as a pattern I believe? That could be used instead for constants.
Literals would always work in-place as constant expressions.
There was a recent langdev Stack Exchange question about this very topic. It’s a bit trickier to design than it might seem at first.
Suppose we require a keyword – say
var
– before all binding patterns. This results in having to write things likefor (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {}
,which is quite a bit more verbose than the current
for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}
.Not to mention you’ll have to write
let var x = 0;
just to declare a variable, unless you redesign the language to allow you to just writevar x = 0
(and if you do that, you’ll also have to somehow support a coherent way to expressif let Some(x) = arr.pop() {}
andlet Some(x) = arr.pop() else {todo!()}
).Suppose we require a keyword – say
const
– before all value-matching patterns that look like variables. Then, what’s currentlymatch (left.next(), right.next()) { (Some(l), Some(r)) => {} (Some(l), None) => {} (None, Some(r)) => {} (None, None) => {} }
turns into either the inconsistently ugly
match (left.next(), right.next()) { (Some(l), Some(r)) => {} (Some(l), const None) => {} (const None, Some(r)) => {} (const None, const None) => {} }
or the even more verbose
match (left.next(), right.next()) { (const Some(l), const Some(r)) => {} (const Some(l), const None) => {} (const None, const Some(r)) => {} (const None, const None) => {} }
and you always run the risk of forgetting a
const
and accidentally binding a new match-all variable namedNone
– the main footgun that syntactically distinguishing binding and value-matching patterns was meant to avoid in the first place.Suppose we require a sigil such as
$
before one type of pattern. Probably the best solution in my opinion, but that’s one symbol that can no longer be used for other things in a pattern context. Also, if you’re already using sigils before variable names for other purposes (I’ve been sketching out a language where a pointer variable$x
can be auto-dereferenced by writingx
), doubling up is really unpleasant.…So I can understand why Rust chose to give the same, most concise possible syntax for both binding and value-matching patterns. At least compiler warnings (unused, non-snake-case variables) are there to provide some protection from accidentally turning one into the other.
I went the “only
let
introduces bindings” route, and I’m pretty happy so far:if (left.next(), right.next()) ... is (Some(let l), Some(let r)) { /* use l and r */ } ... is (Some(let l), None ) { /* use l */ } ... is (None, Some(let r)) { /* use r */ } ... is (None, None ) { /* use nothing */ } }
Yeah, they could literally have the same syntax as now, but w/
let
when introducing a variable. So:match (left.next(), right.next()) { (Some(let l), Some(let r)) => {} (Some(let l), None) => {} (None, Some(let l)) => {} (None, None) => {} }
Or you could put the
let
before theSome(...)
aslet Some(l)
, which allows us to keep the currentif let Some(...) = ...
syntax. Either of those would feel more consistent than the current implementation.I completely forgot that unit structs/variants define their own associated consts. I wonder if in patterns the type can be used instead of the associated const though? That might resolve a lot of the headache. It’d mean changing the way the ident is resolved to looking in the type namespace though.
const <block>
already works as a pattern I believe? That could be used instead for constants.Literals would always work in-place as constant expressions.