Enums with constant values in Rust

2019-02-08 06:45发布

问题:

I can do this:

enum MyEnum {
    A(i32),
    B(i32),
}

but not this:

enum MyEnum {
    A(123), // 123 is a constant
    B(456), // 456 is a constant
}

I can create the structures for A and B with a single field and then implement that field, but I think there might be an easier way. Is there any?

回答1:

The best way to answer this is working out why you want constants in an enum: are you just associating a value with each variant, or do you want each variant to be that value (like an enum in C or C++)?

For the first case, it probably makes more sense to just leave the enum variants with no data, and make a function:

enum MyEnum {
    A,
    B,
}

impl MyEnum {
    fn value(&self) -> i32 {
        match *self {
            MyEnum::A => 123,
            MyEnum::B => 456,
        }
    }
}
// call like some_myenum_value.value()

This approach can be applied many times, to associate many separate pieces of information with each variant, e.g. maybe you want a .name() -> &'static str method too.

Alternatively, for the second case, you can assign explicit tag values just like C/C++:

enum MyEnum {
    A = 123,
    B = 456,
}

This can be matched on in all the same ways, but can also be cast to an integer MyEnum::A as i32. (Note that computations like MyEnum::A | MyEnum::B are not automatically legal in Rust: enums have specific values, they're not bit-flags.)



回答2:

People looking at this may stumble upon the introduction and deprecation of FromPrimitive. A possible replacement which might also be useful here is enum_primitive. It allows you to use C-like enums and have them cast between numeric and logical representation:

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


回答3:

Another alternative is the enum-map crate. This provides the ability to assign a value to the enum record. What is more, you can use this macro with different value types.

#[macro_use]
extern crate enum_map;

use enum_map::EnumMap;

#[derive(Debug, Enum)]
enum Example {
    A,
    B,
    C,
}

fn main() {
    let mut map = enum_map! {
        Example::A => 1,
        Example::B => 2,
        Example::C => 3,
    };
    map[Example::C] = 4;

    assert_eq!(map[Example::A], 1);

    for (key, &value) in &map {
        println!("{:?} has {} as value.", key, value);
    }
}


标签: rust