web-dev-qa-db-fra.com

Comment faire correspondre les valeurs enum avec un entier?

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>;
}
16
dhardy

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 .

12
Renato Zannon

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.

11
wingedsubmariner

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.

5
ideasman42

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

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);
}
0
K. Biermann