Как мне сопоставить значения перечисления с целым числом?
Я могу получить целочисленное значение перечислений, как это:
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>;
}
10 ответов
Since Rust 1.34, I recommend implementing 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(()),
}
}
}
Then you can use TryInto
and handle the possible error:
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"),
}
}
See also:
Вы можете получить FromPrimitive
:
#[macro_use]
extern crate num_derive;
extern crate num_traits;
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),
}
}
Вы можете воспользоваться охраной матчей, чтобы написать эквивалентную, но более грубую конструкцию:
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
не является допустимым значением для перечисления.
Если вы уверены, что значения целого числа включены в перечисление, вы можете использовать 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.
std::num::FromPrimitive
помечен как нестабильный и не будет включен в Rust 1.0. В качестве обходного пути я написал enum_primitive
ящик, который экспортирует макрос enum_from_primitive!
это оборачивает enum
декларация и автоматически добавляет реализацию num::FromPrimitive
(от num
ящик). Пример:
#[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);
}
Если целое число, с которым вы сопоставляете, основано на порядке вариантов перечисления, вы можете использовать strum для генерации итератора перечислений и выбрать правильный:
#[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));
}
В настоящее время я использую этот фрагмент кода для преобразования целых чисел в перечисление:
#[rustfmt::skip]
#[derive(Debug, Clone, Copy)]
pub enum Square {
A8, B8, C8, D8, E8, F8, G8, H8,
A7, B7, C7, D7, E7, F7, G7, H7,
A6, B6, C6, D6, E6, F6, G6, H6,
A5, B5, C5, D5, E5, F5, G5, H5,
A4, B4, C4, D4, E4, F4, G4, H4,
A3, B3, C3, D3, E3, F3, G3, H3,
A2, B2, C2, D2, E2, F2, G2, H2,
A1, B1, C1, D1, E1, F1, G1, H1,
}
impl TryFrom<u64> for Square {
type Error = String;
fn try_from(value: u64) -> Result<Self, Self::Error> {
use Square::*;
#[rustfmt::skip]
const LOOKUP: [Square; 64] = [
A8, B8, C8, D8, E8, F8, G8, H8,
A7, B7, C7, D7, E7, F7, G7, H7,
A6, B6, C6, D6, E6, F6, G6, H6,
A5, B5, C5, D5, E5, F5, G5, H5,
A4, B4, C4, D4, E4, F4, G4, H4,
A3, B3, C3, D3, E3, F3, G3, H3,
A2, B2, C2, D2, E2, F2, G2, H2,
A1, B1, C1, D1, E1, F1, G1, H1,
];
LOOKUP
.get(value as usize)
.ok_or_else(|| {
format!(
"index '{}' is not valid, make sure it's in the range '0..64'",
value
)
})
.map(|s| *s)
}
}
Я не знаю, насколько это быстро, но я, вероятно, узнаю в будущем.
Конечно, недостатком этого является то, что если перечисление будет изменено, его придется обновлять в нескольких местах.
Прочитав вопрос еще пару раз, это не совсем то, о чем просили. Но я пока оставлю этот ответ здесь, так как эта ветка появляется при поиске «rust convert integer to enum». Однако ответ все еще может быть использован для решения проблемы. Просто преобразуйте x в перечисление (
В идеале вы могли бы привести значения перечисления к целым числам:
match x {
MyEnum::A as i32 => {}
MyEnum::B as i32 => {}
MyEnum::C as i32 => {}
_ => {}
}
Однако это не компилируется. Вы не можете использовать выражения (например,as
) в узорах.
Однако вы можете использоватьconsts
в шаблонах и константах можно использовать выражения:
const A: i32 = MyEnum::A as i32;
const B: i32 = MyEnum::B as i32;
const C: i32 = MyEnum::C as i32;
match x {
A => {}
B => {}
C => {}
_ => {}
}
Нестабильная встроенная константа может сделать это намного лучше:
#![feature(inline_const_pat)]
match x {
const { MyEnum::A as i32 } => {}
const { MyEnum::B as i32 } => {}
const { MyEnum::C as i32 } => {}
_ => {}
}
Я написал простой макрос, который преобразует числовое значение обратно в перечисление:
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);
}
Вы можете использовать Strum для созданияfrom_repr
реализация с использованиемFromRepr
получить макрос:
use strum::FromRepr;
#[derive(FromRepr, Debug, PartialEq)]
#[repr(u8)]
enum MyEnum {
A = 1,
B,
C,
}
fn main() {
let e = MyEnum::from_repr(2);
assert_eq!(e, Some(MyEnum::B));
}
Груз.томл
[dependencies]
strum = { version = "0.25", features = ["derive"] }