Je peux obtenir une valeur entière d'un enum comme ceci:
enum MyEnum {
A = 1,
B,
C,
}
let x = MyEnum::C as i32;
mais je n'arrive pas à faire ça:
match x {
MyEnum::A => {}
MyEnum::B => {}
MyEnum::C => {}
_ => {}
}
Comment puis-je concurrencer les valeurs de l'énum ou essayer de convertir x
en MyEnum
?
Je peux voir une fonction comme celle-ci utile pour les énumérations, mais elle n’existe probablement pas:
impl MyEnum {
fn from<T>(val: &T) -> Option<MyEnum>;
}
Vous pouvez dériver FromPrimitive
. Utilisation de Rust 2018, syntaxe d'importation simplifiée:
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),
}
}
Dans votre Cargo.toml
:
[dependencies]
num-traits = "0.2"
num-derive = "0.2"
Plus de détails dans num-derive crate , voir esp. exemple d'utilisations dans les tests .
Vous pouvez tirer parti des gardiens d’allumettes pour écrire une construction équivalente, mais plus dégoûtante:
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
peut également être utilisé:
let y: MyEnum = unsafe { transmute(x as i8) };
Mais cela nécessite que vous connaissiez la taille de l'énum, afin que vous puissiez commencer par transtyper un scalaire approprié, puis que vous produisez un comportement indéfini si x
n'est pas une valeur valide pour l'énum.
Si vous êtes sûr que les valeurs de l'entier sont incluses dans l'énumération, vous pouvez utiliser std::mem::transmute
.
Ceci devrait être utilisé avec #[repr(..)]
pour contrôler le type sous-jacent.
Exemple complet:
#[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"); }
}
}
Notez que contrairement à certaines des autres réponses, cela nécessite uniquement la bibliothèque standard de Rust.
Si l'entier sur lequel vous appariez est basé sur le order des variantes de l'énum, vous pouvez utiliser strum pour générer un itérateur des énumérations et prendre le bon:
#[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));
}
J'ai écrit une macro simple qui convertit la valeur numérique en 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
}
});
}
Vous pouvez l'utiliser comme ceci:
#[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);
}