Weird Enum Casting Behavior [Solved]


#1

Experience has taught me to always ask about compiler issues before deciding something is a bug. Especially when (as in this case) I have seen in consistently in multiple versions. The latest is a current nightly (1abeb436d 2017-12-27).

As part of my ongoing C library conversion, I have a lot of non-Rust-like code in an intermediate state generated by a transpiler, which I will clean up as I go along.

One of those code blocks discovered a behavior that is extremely strange, where enum casting has a different value between setting it in a const variable and setting it in a let variable.

This playground has the complete example, but here is the gist:

    let c_mode : i32 = 2;

    const o: i32 = WriteFlags::Over as i32; // = 0
    const k: i32 = WriteFlags::Keep as i32; // = 1
    const c: i32 = WriteFlags::Concat as i32; // = 2
    let dmode: WriteFlags = match c_mode {
        // compare with o, k, and c, thus decoding the integer from C
    };
    println!("Value of dmode with const: {:?}", dmode); // "Concat", as expected

    let oo: i32 = WriteFlags::Over as i32;
    let kk: i32 = WriteFlags::Keep as i32;
    let cc: i32 = WriteFlags::Concat as i32;
    let dmode: WriteFlags = match c_mode {
        // compare with oo, kk, and cc, thus decoding the integer from C
    };
    println!("Value of dmode with let: {:?}", dmode); // "Over" !?

The compiler is telling me the second example’s first match pattern can match any value, which is why this happens. But it’s not clear to me why when the first example’s match works just fine.

Is this a bug, or am I using Undefined Behavior with that enum cast – which means this is a “feature”? :slight_smile:


#2

Heed the warnings:

warning: unreachable pattern
  --> src/main.rs:29:9
   |
29 |         kk => WriteFlags::Keep,
   |         ^^ this is an unreachable pattern
   |
   = note: #[warn(unreachable_patterns)] on by default
note: this pattern matches any value
  --> src/main.rs:28:9
   |
28 |         oo => WriteFlags::Over,
   |         ^^

warning: unreachable pattern
  --> src/main.rs:30:9
   |
30 |         cc => WriteFlags::Concat,
   |         ^^ this is an unreachable pattern
   |
note: this pattern matches any value
  --> src/main.rs:28:9
   |
28 |         oo => WriteFlags::Over,
   |         ^^

warning: unreachable pattern
  --> src/main.rs:31:9
   |
31 |         n => panic!("illegal crput flag: {}", n),
   |         ^ this is an unreachable pattern
   |
note: this pattern matches any value
  --> src/main.rs:28:9
   |
28 |         oo => WriteFlags::Over,
   |         ^^

Basically, this line doesn’t do what you think it does:

        oo => WriteFlags::Over,

this does not match against the value of the local oo variable. This matches against anything, and binds it to the name oo. For example, this is equivalent:

    let dmode: WriteFlags = match c_mode {
        o => WriteFlags::Over,
    };

Does that make sense?


#3

Now I see it: I’m masking my own variables in the match statement itself. As much as I went through to get the code to compile, I never “saw it” that way.

That explains everything. Thanks.


#4

Having been bitten by exactly this same warning, and being equally confused at the time, you have my sympathies!

(P.s. I took the liberty to add “solved” to the title)