Rust: C-style enum doesn't work in match arm

Created on 2 Sep 2017  路  7Comments  路  Source: rust-lang/rust

I expected this to work, but it doesn't:

enum Thing {
    Foo = 0,
    Bar = 1,
}

fn main() {
    // works
    println!("Foo is {}", Thing::Foo as u8);

    // doesn't
    match 0u8 {
        Thing::Foo as u8 => println!("0 is Foo"),
        _ => println!("eh")
    };
}

with:

error: expected one of `::`, `=>`, `if`, or `|`, found `as`
  --> src/main.rs:11:20
   |
11 |         Thing::Foo as u8 => println!("0 is Foo"),
   |                   -^^ unexpected token
   |                   |
   |                   expected one of `::`, `=>`, `if`, or `|` here

Putting the cast inside extra parens doesn't help.

My use case is having an enum whose ordinals match an external protocol definition, then matching against the value read off the wire to convert into a member of the enum. I would expect this to be super common.

Previous history:

  • #3580 which is precisely this issue, but is closed as duplicating:
  • #2132 which is not the same thing (in fact, the opposite). Continued to:
  • #3868 which introduced a bunch of traits to make this work; but now deprecated and removed
C-feature-request T-lang

Most helpful comment

Better workaround:

#[macro_use] extern crate enum_primitive_derive;
extern crate num_traits;
use num_traits::FromPrimitive;

#[derive(Primitive)]
enum Thing {
    Foo = 0,
    Bar = 1,
}

match Thing::from_u8(0) {
    Some(Thing::Foo) => ...,
    Some(Thing::Bar) => ....,
    None => panic!("out of range"),
}

All 7 comments

In fact, this has little to do with C-style enums per se. Instead, it's casts which aren't allowed in match arms: see eg #11791. That's... less than ideal.

From that issue, the workaround seems to be:

enum Thing {
    Foo = 0,
    Bar = 1,
}

const THING_FOO: u8 = Thing::Foo as u8;
const THING_BAR: u8 = Thing::Bar as u8;

fn main() {
    // works
    println!("Foo is {}", Thing::Foo as u8);

    // doesn't
    match 0u8 {
        THING_FOO => println!("0 is Foo"),
        _ => println!("eh")
    };
}

That's doable but pretty annoying and unergonomic.

I think the following is the more widely used workaround for that:

match 0u8 {
    x if x == Thing::Foo as u8 => { ... }
    x if x == Thing::Bar as u8 => { ... }
    _ => println!("..."),
}

(Which obviously is even worse since you easily can get overlapping variants and lose
most benefits of enums in the first place)

Better workaround:

#[macro_use] extern crate enum_primitive_derive;
extern crate num_traits;
use num_traits::FromPrimitive;

#[derive(Primitive)]
enum Thing {
    Foo = 0,
    Bar = 1,
}

match Thing::from_u8(0) {
    Some(Thing::Foo) => ...,
    Some(Thing::Bar) => ....,
    None => panic!("out of range"),
}

Thing::Foo as u8

I think this explicitly SHOULD NOT work. In particular, I think we should be working towards the deprecation of the as operator and so extending support for it would be a step in the wrong direction.

This is a feature request that sounds like it'll be a heavily discussed RFC. You should close it here and open an issue on the RFC repo or the internals forum

Yup, agreed with @oli-obk . Please pursue this through the usual RFC-ish channels, thank you!

Was this page helpful?
0 / 5 - 0 ratings