98

I can get an integer value of an enums like this:

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

let x = MyEnum::C as i32;

but I can't seem to do this:

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

How can I either match against the values of the enum or try to convert x back to a MyEnum?

I can see a function like this being useful for enums, but it probably doesn't exist:

impl MyEnum {
    fn from<T>(val: &T) -> Option<MyEnum>;
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dhardy
  • 11,175
  • 7
  • 38
  • 46
  • 10
    You may also want to explain why you want to do this. I'd suggest that you keep things as a enum throughout, and maybe have a parse step that converts back if if really need it. – Shepmaster Jan 19 '15 at 16:19
  • 14
    C error codes: I'm calling a C function which may return an error as an integer. I want my error codes to be compatible. I could of course use an enum handling each specifically, but that's extra code. – dhardy Jan 20 '15 at 09:26
  • On second thoughts, adapting `MyEnum` to `enum MyEnum { A = 1, B, C, Other(i32) }` is probably a better solution for my problem. I'll leave the original question up for other users, however (I have certainly wanted to do this before, whether out of bad habits or lack of good alternatives I do not know). – dhardy Jan 20 '15 at 11:26
  • 1
    Make that something like `enum Codes { A = 1, B, C }; enum MyEnum { Code(Codes), Other(i32) };`. – dhardy Jan 20 '15 at 11:34
  • @shepmaster same here, I am decoding instructions. I proudly typed the meanings of a nibble into an enum (thinking "I am a good rust person, no #defines for me") and got told nope. I guess I just have to use a set of consts – pm100 Aug 12 '20 at 00:07

10 Answers10

77

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

If you have a great number of variants, a macro can be used to create a parallel implementation of TryFrom automatically based on the definition of the enum:

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 $name {
            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,
    }
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    I can't understand, looks like magic. `x` has type `i32` right? Then how does Rust know that it must be matched against `MyEnum` (and not some other enum that also implemented the trait) variants? – Nurbol Alpysbayev Nov 11 '19 at 16:16
  • 3
    @NurbolAlpysbayev it must use the implementation of `TryInto` that corresponds to `MyEnum` because that's the type of the value that is used in the match arms (`Ok(MyEnum::A)`). [How does Rust infer resultant types from From::<>::from()?](https://stackoverflow.com/q/44737154/155423); [How does Rust's type inference work across multiple statements?](https://stackoverflow.com/q/37542971/155423) – Shepmaster Nov 11 '19 at 16:20
  • I'll read the links, but in short, Rust looks at the first arm and makes sure the following arms are with the same enum type (and then also ensures exhaustiveness), am I right? [Looks](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bcae7c7ac3c4bdc8a64c2710ef9e7cd8) like this is the case. – Nurbol Alpysbayev Nov 11 '19 at 16:22
  • Unfortunately, I've found that this solution may have a disatvantage in that exhaustiveness is not checked in the `try_from` function. So basically it's not reliable. – Nurbol Alpysbayev Nov 11 '19 at 16:42
  • @NurbolAlpysbayev you seem to be using multiple (non-standard?) meanings for the word "exhaustive". You may be interested in [How to ensure every enum variant can be returned from a specific function at compile time?](https://stackoverflow.com/q/58715081/155423). – Shepmaster Nov 11 '19 at 16:45
  • I believe I use the meaning from the [Rust Book](https://doc.rust-lang.org/1.30.0/book/second-edition/ch06-02-match.html#matches-are-exhaustive). Regarding the link, I am not sure how that can be used. Anyway, the top voted answer seems to work. I just wish there'd be a zero-dependency, reliable (with exhaustiveness check) and preferably concise solution. – Nurbol Alpysbayev Nov 11 '19 at 16:50
  • To be clear, the derived implementation of `FromPrimitive` looks the same as the `TryFrom` implementation in this answer (using `Option` instead of `Result`, but otherwise...) – Shepmaster Nov 11 '19 at 16:53
  • I may really miss something, but I tested that, and no, it doesn't look the same in terms of reliability. Just remove `x if x == MyEnum::B as i32 => Ok(MyEnum::B)` line and you will get nothing but a "dead code" warning. I posted the link to the playground somewhere above. I really wished I could use some "standard", reliable approach, but this is not it, probably. – Nurbol Alpysbayev Nov 11 '19 at 16:57
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202158/discussion-between-shepmaster-and-nurbol-alpysbayev). – Shepmaster Nov 11 '19 at 16:58
  • It would be nice if Rust had something built in to convert the integer back to the enum (given that the assignment to ints is also built in). Writing the `try_from` like this is verbose and error prone... – User Jan 26 '21 at 18:54
  • Ah, missed the macro. Something built in would still be better. – User Jan 26 '21 at 18:58
64

You can derive FromPrimitive. Using Rust 2018 simplified imports syntax:

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

In your Cargo.toml:

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

More details in num-derive crate, see esp. sample uses in tests.

akavel
  • 4,789
  • 1
  • 35
  • 66
Renato Zannon
  • 28,805
  • 6
  • 38
  • 42
41

You can take advantage of match guards to write an equivalent, but clunkier, construction:

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 can also be used:

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

But this requires that you know the size of the enum, so you can cast to an appropriate scalar first, and will also produce undefined behavior if x is not a valid value for the enum.

wingedsubmariner
  • 13,350
  • 1
  • 27
  • 52
  • 2
    Match-guards is a good last-resort idea. Assuming a representation of the enum in memory is however something I really don't want to do — it could well break with some new Rust version. Avoiding unsafe code is the biggest reason for using Rust in the first place! – dhardy Jan 20 '15 at 09:29
  • 1
    I'd probably do this and wrap it up as a method on `MyEnum`. Then you can call `MyEnum::from_integer` or whatever. – Shepmaster Feb 20 '15 at 22:13
  • this gets my vote, assuming all you have is a list of enum variants with values assigned in the definition then if you've casted out the number in the first place theres no logical reason not to be able to map it back again. It also means that you can iterate over simple enums without the need for external crates. – S.W. Jan 01 '22 at 18:11
32

If you're sure the values of the integer are included in the enum, you can use std::mem::transmute.

This should be used with #[repr(..)] to control the underlying type.

Complete Example:

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

Note that unlike some of the other answers, this only requires Rust's standard library.

Community
  • 1
  • 1
ideasman42
  • 42,413
  • 44
  • 197
  • 320
  • 2
    [This answer](http://stackoverflow.com/a/28029667/155423) already mentions `transmute` and only requires the standard library. Seems like this answer should just be a comment on that answer to recommend using `repr`. – Shepmaster Feb 22 '17 at 03:33
  • 1
    @Shepmaster, I considered editing or suggesting edits to the other answer. But think it would have made it into a different answer. Whatever the case, I don't mind if this answer is copy-pasted into another, it just seems a bit odd to do so. – ideasman42 Feb 22 '17 at 03:58
  • 6
    Using an unsafe function to do something as simple as this seems like a very bad practice... – Sean Burton Aug 19 '17 at 12:18
  • 1
    @sean-burton - agree you wouldn't want your code riddled with unsafe transmute, OTOH - if your interfacing a C library, you don't always have an option besides writing some lookup table which is tedious, needs to be kept in sync and possibly introduces human error. So AFAICS this is the lesser of two evils. – ideasman42 Aug 19 '17 at 13:35
  • 1
    @SeanBurton Writing a switch that is essentially mapping 1:1, 2:2, 3:3 doesn't seem like great practice either. – c z Dec 01 '22 at 11:29
16

std::num::FromPrimitive is marked as unstable and will not be included in Rust 1.0. As a workaround, I wrote the enum_primitive crate, which exports a macro enum_from_primitive! that wraps an enum declaration and automatically adds an implementation of num::FromPrimitive (from the num crate). Example:

#[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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Anders Kaseorg
  • 3,657
  • 22
  • 35
9

If the integer you are matching on is based on the order of the variants of the enum, you can use strum to generate an iterator of the enum variants and take the correct one:

#[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));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
3

I'm currently using this piece of code to convert from integers to an enum:

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

I don't know how quick this is, but I'm probably going to find out in the future.

Of course, a drawback of this is that if the enum gets changed, it will have to be updated in multiple places.

After reading the question a couple more times, this isn't really what was asked for. But I'll leave this answer here for now, as this thread shows up when searching for "rust convert integer to enum". The answer could still be used to solve the problem though. Simply convert x to the enum (x.try_into().unwrap() (you actually shouldn't really just unwrap, but handle the error)), and then use the normal match statement with the enum variants.

Elias
  • 3,592
  • 2
  • 19
  • 42
2

I wrote a simple macro which converts the numerical value back to the 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
        }
    });
}

You can use it like this:

#[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);
}
K. Biermann
  • 1,295
  • 10
  • 22
  • 1
    This macro forces you to repeat in its invocation all the relevant enum variants. It is better to give the macro the enum definition, so it can build something that knows about all the possible variants. – hkBst Mar 17 '22 at 13:26
2

You can use strum to generate from_repr implementation using FromRepr derive macro:

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

Cargo.toml

[dependencies]
strum = { version = "0.25", features = ["derive"] }
nazgul
  • 449
  • 1
  • 4
  • 10
2

Ideally, you would be able to cast the enum values to integers:

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

However this doesn't compile. You cannot use expressions (like as) in patterns.

However, you can use consts in patterns, and consts can use expressions:

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 => {}
    _ => {}
}

The unstable inline const can make this a lot nicer:

#![feature(inline_const_pat)]

match x {
    const { MyEnum::A as i32 } => {}
    const { MyEnum::B as i32 } => {}
    const { MyEnum::C as i32 } => {}
    _ => {}
}
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77