Indexing an array with enum variants

I want to index an array with an enum - with enum of primitives the Index trait can be implemented like so:

enum Obj {
    Gold=0,
    Silver,
    Emerald
}
const N_OBJECTS: usize = Obj::Emerald as usize + 1;

impl<T> Index<Obj> for [T; N_OBJECTS] {
    type Output = T;
    fn index(&self, idx: Obj) -> &Self::Output {
        &self[idx as usize]
    }
}
impl<T> IndexMut<Obj> for [T; N_OBJECTS] {
    fn index_mut(&mut self, idx: Obj) -> &mut Self::Output {
        &mut self[idx as usize]
    }
}

let a: [bool; N_OBJECTS] = [true; N_OBJECTS];
println!("{:?}", a[Obj::Gold]);

How to do this if some of the enum variants are not not primitive? (In this case it would be ok to only have one index value for Emerald(u8)).

enum Obj {
    Gold=0,
    Silver=1,
    Emerald(u8),
}

You could create a derive macro that implements a trait for getting a variant index by matching on &self.

1 Like

You could also create another fieldless enum with the same variant names, if this is something you want to store in a typed fashion. There exists crates that can do this for you.

2 Likes

Here's another popular crate that does this for enums whose variants are all primitives: enum_map

And yeah, you won't be able to index on a non-primitive, but you could define a mapping function to a separate enum to use this way.

1 Like

I feel like everyone is overcomplicating things with derive macros and so on. What's wrong with a match statement?

use std::ops::Index;

enum Obj {
    Gold,
    Silver,
    Emerald(u8),
}

impl<T> Index<Obj> for [T] {
    type Output = T;
    fn index(&self, idx: Obj) -> &Self::Output {
        match idx {
            Obj::Gold => &self[0],
            Obj::Silver => &self[1],
            Obj::Emerald(index) => &self[index as usize],
        }
    }
}

(playground)

2 Likes

Many thanks - I like this solution in general.

In the actual problem I have reverted to primitive enums.
There are about 100 variants and I don't like to list them individually a 2nd time.

The external crate solutions make it easier to do without bloating the visible source, but don't like to resort to an external crate for a core language issue like this. Also, it is not without cost.

What's wrong with it is that you now have to maintain the types at two places, and risk introducing a bug if you forget to update the match if eg. you swap the order of two variants.

A derive-based solution would automatically eliminate this sort of logic error.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.