Enum Behavior With/Without Fully Qualified


#1

I ran into a case with enums and I am not sure what exactly is happening. Here is an example demonstrating my confusion. The first match always matches the first case. The second match works correctly.

enum Foo {
One,
Two,
Three
}

fn main() {
let f1 = Foo::Three;
match f1 {
One => { println!(“ONE”); },
Two => { println!(“TWO”); },
Three => { println!(“THREE”); }
};

let f2 = Foo::Three;
match f2 {
    Foo::One => { println!("ONE"); },
    Foo::Two => { println!("TWO"); }:

    Foo::Three => { println!("THREE"); }
};

}

Or link: https://is.gd/0GTGBz

There are two big questions here:

  1. Why does the enum require the cases to be fully qualified for it to work? It compiles so I assume it knows the type.
  2. Given the undesired behavior, how is the program even compiling?

#2

The form

match f1 {
  One => { body… }
}

moves (I believe) f1 to a new variable One for the duration of the match body, much like let One = f1 would. A pattern consisting of a single variable name always matches. It’s pure coincidence, as far as Rust is concerned, that the variable name happens to be the same as the name of one of the enum constants in Foo.

Rust namespaces enum value constructors to the enum type name by default: the values are named like Foo::One, not like One, everywhere outside of the enum body. You can import constants, though: use Foo::One will import One into the current scope, or use Foo::* will import all of the enum constants from Foo into the current scope, allowing you to use them without qualification. Rust’s own prelude does this to put the value constructors for Option<T> in scope, so that you can type Ok(v) rather than Option::Ok(v).


#3

Rust even warns you that you might be trying to match on enum variants, not bind to a similarly-named variable:

rustc 1.16.0 (30cf806ef 2017-03-10)
warning[E0170]: pattern binding `One` is named the same as one of the variants of the type `Foo`
  --> <anon>:10:9
   |
10 |         One => { println!("ONE"); },
   |         ^^^
   |
   = help: if you meant to match on a variant, consider making the path in the pattern qualified: `Foo::One`

warning[E0170]: pattern binding `Two` is named the same as one of the variants of the type `Foo`
  --> <anon>:11:9
   |
11 |         Two => { println!("TWO"); },
   |         ^^^
   |
   = help: if you meant to match on a variant, consider making the path in the pattern qualified: `Foo::Two`

warning[E0170]: pattern binding `Three` is named the same as one of the variants of the type `Foo`
  --> <anon>:12:9
   |
12 |         Three => { println!("THREE"); }
   |         ^^^^^
   |
   = help: if you meant to match on a variant, consider making the path in the pattern qualified: `Foo::Three`

#4

Thank you for explaining that! You are correct. Adding “use Foo:*” fixes the problem. I did not realize that ‘One’ was being created as a new variable like that.

The compiler message makes it sound more like “hey I think this is what you meant, but you should qualify it to be sure” instead of “hey you have a shadow variable here that is going to introduce an annoying bug”.