How do I match enum values with an integer?
我可以得到一个枚举的整数值,如下所示:
1 2 3 4 5 6 7 | enum MyEnum { A = 1, B, C, } let x = MyEnum::C as i32; |
但我似乎无法做到这一点:
1 2 3 4 5 6 | match x { MyEnum::A => {} MyEnum::B => {} MyEnum::C => {} _ => {} } |
如何与枚举值匹配或尝试将
我可以看到这样的函数对枚举很有用,但它可能不存在:
1 2 3 | impl MyEnum { fn from< T >(val: &T) -> Option<MyEnum>; } |
您可以派生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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), } } |
在您的
1 2 3 | [dependencies] num-traits ="0.2" num-derive ="0.2" |
num num crate板条箱中的更多详细信息,请参见esp。测试中的样本用途。
自Rust 1.34起,我建议实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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(()), } } } |
然后您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 | 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"), } } |
如果您有大量的变体,可以使用一个宏根据枚举的定义自动创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | macro_rules! back_to_enum { ($(#[$meta:meta])* $vis:vis enum $name:ident { $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* }) => { $(#[$meta])* $vis enum $name { $($(#[$vmeta])* $vname $(= $val)?,)* } impl std::convert::TryFrom<i32> for MyEnum { type Error = (); fn try_from(v: i32) -> Result<Self, Self::Error> { match v { $(x if x == $name::$vname as i32 => Ok($name::$vname),)* _ => Err(()), } } } } } back_to_enum! { enum MyEnum { A = 1, B, C, } } |
另请参见:
- 如何确保在编译时可以从特定函数返回每个枚举变量?
- 在宏中生成文档
您可以利用比赛后卫来编写等效但笨拙的构造:
1 2 3 4 5 6 | match x { x if x == MyEnum::A as i32 => ..., x if x == MyEnum::B as i32 => ..., x if x == MyEnum::C as i32 => ..., _ => ... } |
1 | let y: MyEnum = unsafe { transmute(x as i8) }; |
但是这要求您知道枚举的大小,因此您可以先强制转换为适当的标量,并且如果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #[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); } |
如果您确定整数值包含在枚举中,则可以使用
应与
完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #[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的标准库。
如果要匹配的整数基于枚举变量的顺序,则可以使用strum生成枚举变量的迭代器,并采用正确的迭代器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #[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)); } |
我写了一个简单的宏,它将数值转换回枚举:
1 2 3 4 5 6 7 8 | 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 } }); } |
您可以像这样使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #[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); } |