如何将枚举值与整数匹配?

时间:2015-01-19 16:07:30

标签: rust

我可以得到这样的枚举的整数值:

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

let x = MyEnum::C as i32;

但我似乎无法做到这一点:

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

如何匹配枚举值或尝试将x转换回MyEnum

我可以看到这样的函数对枚举有用,但它可能不存在:

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

7 个答案:

答案 0 :(得分:13)

您可以派生FromPrimitive。使用Rust 2018简化导入语法:

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),
    }
}

Cargo.toml

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

num-derive crate中的更多详细信息,请参阅esp。 sample uses in tests

答案 1 :(得分:12)

你可以利用比赛防守来编写一个等同但更笨重的结构:

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也可以使用:

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

但是这需要您知道枚举的大小,因此您可以首先转换为适当的标量,如果x不是枚举的有效值,也会产生未定义的行为。

答案 2 :(得分:11)

std::num::FromPrimitive标记为unstable,不会包含在Rust 1.0中。作为解决方法,我编写了 enum_primitive crate ,它导出了一个包含enum_from_primitive!声明的宏enum并自动添加了num::FromPrimitive的实现(来自num crate)。例如:

#[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);
}

答案 3 :(得分:7)

自Rust 1.34起,我建议实施TryFrom

use std::convert::TryFrom;

impl TryFrom<i32> for MyEnum {
    type Error = ();

    fn try_from(v: i32) -> Result<Self, Self::Error> {
        match v {
            x if x == MyEnum::A as i32 => Ok(MyEnum::A),
            x if x == MyEnum::B as i32 => Ok(MyEnum::B),
            x if x == MyEnum::C as i32 => Ok(MyEnum::C),
            _ => Err(()),
        }
    }
}

然后您可以使用TryInto并处理可能的错误:

use std::convert::TryInto;

fn main() {
    let x = MyEnum::C as i32;

    match x.try_into() {
        Ok(MyEnum::A) => println!("a"),
        Ok(MyEnum::B) => println!("b"),
        Ok(MyEnum::C) => println!("c"),
        Err(_) => eprintln!("unknown number"),
    }
}

另请参阅:

答案 4 :(得分:5)

如果您确定整数的值包含在枚举中,则可以使用std::mem::transmute

这应该与#[repr(..)]一起使用来控制基础类型。

完整示例:

#[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"); }
    }
}

请注意,与其他一些答案不同,这只需要Rust的标准库。

答案 5 :(得分:1)

我写了一个简单的宏,它将数值转换回枚举:

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
        }
    });
}

您可以像这样使用它:

#[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);
}

答案 6 :(得分:0)

如果您匹配的整数基于枚举变体的 order ,则可以使用strum生成枚举的迭代器并采用正确的迭代器:

@Bean
public MongoCustomConversions mongoCustomConversions() {
    return new MongoCustomConversions(Arrays.asList(
        new BigDecimalDecimal128Converter(),
        new Decimal128BigDecimalConverter()
    ));

}

@WritingConverter
private static class BigDecimalDecimal128Converter implements Converter<BigDecimal, Decimal128> {

    @Override
    public Decimal128 convert(@NonNull BigDecimal source) {
        return new Decimal128(source);
    }
}

@ReadingConverter
private static class Decimal128BigDecimalConverter implements Converter<Decimal128, BigDecimal> {

    @Override
    public BigDecimal convert(@NonNull Decimal128 source) {
        return source.bigDecimalValue();
    }

}