How do I match enum values with an integer?

2019-01-15 13:00发布

I can get an integer value of an enums like this:

enum MyEnum {
    A = 1,
    B,
    C,
}

let x = MyEnum::C as i32;

but I can't seem to do this:

match x {
    MyEnum::A => {}
    MyEnum::B => {}
    MyEnum::C => {}
    _ => {}
}

How can I either match against the values of the enum or try to convert x back to a MyEnum?

I can see a function like this being useful for enums, but it probably doesn't exist:

impl MyEnum {
    fn from<T>(val: &T) -> Option<MyEnum>;
}

标签: rust
6条回答
啃猪蹄的小仙女
2楼-- · 2019-01-15 13:03

I wrote a simple macro which converts the numerical value back to the enum:

macro_rules! num_to_enum {
    ($num:expr => $enm:ident<$tpe:ty>{ $($fld:ident),+ }; $err:expr) => ({
        match $num {
            $(_ if $num == $enm::$fld as $tpe => { $enm::$fld })+
            _ => $err
        }
    });
}

You can use it like this:

#[repr(u8)] #[derive(Debug, PartialEq)]
enum MyEnum {
    Value1 = 1,
    Value2 = 2
}

fn main() {
    let num = 1u8;
    let enm: MyEnum = num_to_enum!(
        num => MyEnum<u8>{ Value1, Value2 };
        panic!("Cannot convert number to `MyEnum`")
    );
    println!("`enm`: {:?}", enm);
}
查看更多
啃猪蹄的小仙女
3楼-- · 2019-01-15 13:07

You can derive FromPrimitive. Using Rust 2018 simplified imports syntax:

use num_derive::FromPrimitive;    
use num_traits::FromPrimitive;

#[derive(FromPrimitive)]
enum MyEnum {
    A = 1,
    B,
    C,
}

fn main() {
    let x = 2;

    match FromPrimitive::from_i32(x) {
        Some(MyEnum::A) => println!("Got A"),
        Some(MyEnum::B) => println!("Got B"),
        Some(MyEnum::C) => println!("Got C"),
        None            => println!("Couldn't convert {}", x),
    }
}

In your Cargo.toml:

[dependencies]
num-traits = "0.2"
num-derive = "0.2"

More details in num-derive crate, see esp. sample uses in tests.

查看更多
Anthone
4楼-- · 2019-01-15 13:12

If you're sure the values of the integer are included in the enum, you can use std::mem::transmute.

This should be used with #[repr(..)] to control the underlying type.

Complete Example:

#[repr(i32)]
enum MyEnum {
    A = 1, B, C
}

fn main() {
    let x = MyEnum::C;
    let y = x as i32;
    let z: MyEnum = unsafe { ::std::mem::transmute(y) };

    // match the enum that came from an int
    match z {
        MyEnum::A => { println!("Found A"); }
        MyEnum::B => { println!("Found B"); }
        MyEnum::C => { println!("Found C"); }
    }
}

Note that unlike some of the other answers, this only requires Rust's standard library.

查看更多
三岁会撩人
5楼-- · 2019-01-15 13:13

You can take advantage of match guards to write an equivalent, but clunkier, construction:

match x {
    x if x == MyEnum::A as i32 => ...,
    x if x == MyEnum::B as i32 => ...,
    x if x == MyEnum::C as i32 => ...,
    _ => ...
}

std::mem::transmute can also be used:

let y: MyEnum = unsafe { transmute(x as i8) };

But this requires that you know the size of the enum, so you can cast to an appropriate scalar first, and will also produce undefined behavior if x is not a valid value for the enum.

查看更多
Lonely孤独者°
6楼-- · 2019-01-15 13:19

If the integer you are matching on is based on the order of the variants of the enum, you can use strum to generate an iterator of the enums and take the correct one:

#[macro_use]
extern crate strum_macros; // 0.9.0
extern crate strum;        // 0.9.0

use strum::IntoEnumIterator;

#[derive(Debug, PartialEq, EnumIter)]
enum MyEnum {
    A = 1,
    B,
    C,
}

fn main() {
    let e = MyEnum::iter().nth(2);
    assert_eq!(e, Some(MyEnum::C));
}
查看更多
兄弟一词,经得起流年.
7楼-- · 2019-01-15 13:24

std::num::FromPrimitive is marked as unstable and will not be included in Rust 1.0. As a workaround, I wrote the enum_primitive crate, which exports a macro enum_from_primitive! that wraps an enum declaration and automatically adds an implementation of num::FromPrimitive (from the num crate). Example:

#[macro_use]
extern crate enum_primitive;
extern crate num;

use num::FromPrimitive;

enum_from_primitive! {
    #[derive(Debug, PartialEq)]
    enum FooBar {
        Foo = 17,
        Bar = 42,
        Baz,
    }
}

fn main() {
    assert_eq!(FooBar::from_i32(17), Some(FooBar::Foo));
    assert_eq!(FooBar::from_i32(42), Some(FooBar::Bar));
    assert_eq!(FooBar::from_i32(43), Some(FooBar::Baz));
    assert_eq!(FooBar::from_i32(91), None);
}
查看更多
登录 后发表回答