Nice and efficient idiom for Array indexed by c-like enum?


#1

Hi all,

I’m wondering how I could nicely and efficiently implement a simple array mapping from a simple finite enum such as

enum Status { Good, Bad, Ugly }

to some other type. I could do this by implementing Index with a big match, but that sounds clumsy and likely to not compile to a simple index. I could implement Index with an as usize, but then I wonder if it will end up doing bounds checks. Neither sounds pretty.

Any suggestions for an idiomatic way to express this?


#2

If you want map to you can do simple thing:

enum Status {
    Good = 0, Bad = 1, Ugly = 2
}

and then you can write:

let a = Status::Bad;
let b = a as u8;

and you can use any integer type instead of u8.


#3

Ada language allows that kind of indexing in a perfectly safe and efficient way (the type of the array is indexable only with the correct type of index), with short code and syntactic convenience. Rust (and D language) are still sorely lacking on this… I’d like a RFC on this.

In Rust I guess you can use an unsafe indexing function that avoids the bounds checks.


#4

Unless it got reverted, the compiler should be telling LLVM the maximum value of the enum so bounds checks can be elided.


#5

That’s encouraging, @DroidLogician! For those who might be curious (and any additional advice), I’ve implemented an array mapping an enum below. The only ugliness remaining is that I have to hard-code the size of the array, since I can’t access “Status::Dirty as usize” as a constant.

#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub enum Status {
    Unknown,
    BeingDetermined,
    Clean,
    Built,
    Building,
    Failed,
    Marked,
    Unready,
    Dirty,
}

pub struct StatusMap<T>( [T;9] );

impl<T> StatusMap<T> {
    fn new<F>(v: F) -> StatusMap<T>
        where F: Fn() -> T
    {
        StatusMap( [v(),v(),v(),v(),v(),v(),v(),v(),v()] )
    }
}

impl<T> std::ops::Index<Status> for StatusMap<T>  {
    type Output = T;
    fn index(&self, s: Status) -> &T {
        &self.0[s as usize]
    }
} 

Thanks again for the helpful advice!