I was wondering why the compiler can't infer the "namespace" of a match arm when matching Enums. My intuition makes me believe that since it knows what type its matching against that it could assume that the options it gets are variants of the enum, so the example bellow could be compiled.
I mean, you could change the language so that enum variants are automatically in scope when matching on them. But it would be an exception to the rules for when things are in scope, and the language doesn't have any other exceptions of that kind at all. Not having exceptions to the rules usually makes the language simpler.
But you can do this:
enum Foo {
Value,
AnotherValue
}
fn print_foo(foo: Foo) {
use Foo::*;
match foo {
Value => println!("avalue"),
AnotherValue => println!("anothervalue"),
}
}
because patterns are complicated, they are not just enum variants, and it will cause ambiguity. particularly in your example, a single identifier is a treated as a variable binding. (rust doesn't distinguish upper case identifiers from lower case identifiers, unlike certain languages like Haskell, where the upper case identifier is not a variable).
e.g. the following code is perfectly valid:
EDIT:
you'll actually get an E0170 diagnosis if you bind to a variable with the same name of the variant, but the idea of being ambiguous still holds, to quote the documentation for E0170:
If you don't qualify the names, the code will bind new variables named "GET" and "POST" instead. This behavior is likely not what you want, so rustc warns when that happens.
enum Foo {
Value,
AnotherValue
}
fn foo(foo: Foo) {
match foo {
Foo::Value => println!("Foo::Value"),
// EDIT:
// use a variable named `Value` triggers E0170
/*
Value => println!("bind to a variable named `Value`"),
*/
// you can still bind to a variable named `Value`,
// just the "simple variable" pattern is checked for E0170
Value@_ => println!("bind to a variable named `Value`"),
// you can also bind a reference
ref Value => println!("bind ref to variable named `Value`"),
}
}
So maybe I'm not understanding how match works. If I'm matching against an Enum type, how can it expect a completely unrelated variable ? The possibilities are known, so much that it can check if you handled them all.
It’s not an already existing variable, it’s a new variable that is only accessible to the specific match branch. This is equivalent:
enum Foo {
Value,
AnotherValue
}
fn foo(foo: Foo) {
match foo {
Foo::Value => println!("Foo::Value"),
my_var => println!("bind to a variable named `my_var` with value {:?}", my_var),
}
}
However, I am a big fan of the following extension to the language:
there are different kinds of patterns, destructured enum is just one of them.
also, there are many places where a pattern can be matched, match arms is just one of them.
most notably, a let binding is actually pattern matching (just the pattern must be non-refutable). likewise if let or while let can be used to match refutable patterns. function parameters (e.g. see this discussion) are pattern matching too.