I asked on stackoverflow about idiomatic ways to use values computed at runtime as patterns in match expressions:
I have a value (byte) that I need to match against other values. Some of the values are fixed (b'0'..b'9'). Others are computed at runtime (c = some_function()).
My current solution is to use a fake variable and a if guard (i.e. k if (k == c)), but it does not look very nice to me. I tried using just c but that gets interpreted as a catch-all variable, rather than substituted for the value of c in that context.
Couldn't the rules for matching inside match be changed to use the content of an already bound variable when it is used inside as a pattern?
That would allow me to write
let target_char = CHARS[i];
let msg = match byte {
b'0'..b'9' => "numeric",
target_char => "same char", // <-- I would had preferred this
_ => "different char",
};
instead of
let target_char = CHARS[i];
let msg = match byte {
b'0'..b'9' => "numeric",
k if (k == target_char) => "same char",
_ => "different char",
};
The former is more concise and it also looks simpler to me. Is the style in the second snippet forced "by design" or maybe Rust will change in the future to allow also the style in the first snippet?
It adds a fourth meaning of an identifier in a pattern besides unit struct match, variable binding and match with a static. The case convention would also conflict with bindings, making it locally impossible to judge.
It makes match depend on a trait (PartialEq) to compare the equality
That dependency also means user code can run during matching without an explicit if clause, which so far is being avoided.
Exhaustiveness checking would have to consider it as a refutable pattern, which means a match on a identifier gets an third exhaustiveness mode (besides “can match and removes this case from all possibilities” and “always matches”)
All in all, it just adds an special case to match and makes it more complicated to reason about, which I think is a bad idea.
However, what might be a bit more viable is to extend match to a syntactic shortform for predicates like this:
let target_char = CHARS[i];
let msg = match byte {
b'0'..b'9' => "numeric",
if @ == target_char => "same char",
_ => "different char",
};
Here, if expr( ... @ ... ) would be the predicate short form and desugar to TMP if expr (... TMP ... )
However, it would be also possible to implement either this or the == special case as a macro:
Allowing local variables to be used directly in matches is likely to lead to very confusing bugs where match arms are surprisingly skipped, e.g.
enum Foo {
A(int), B(f64), C, D
}
let x = 1;
// ... lines of code ...
match foo {
// trying to bind a new name `x`
A(x) => println!("A: {}", x),
B(y) => println!("B: {}", y),
_ => println!("other")
}
If variables could be used as patterns, that code would basically be match foo { A(1) => ... and hence foo = A(2) would print other: very different to the intended behaviour. We already somewhat have this problem with statics and match, but the naming conventions help mitigate it; this is not the case with local variables.
(This is possibly along the lines of what @Kimundi meant by “impossible to judge”.)