Using enum to name list indices

Consider the following code where I am trying to use the enum Field to look-up indices in the data list.

#[repr(usize)]
#[derive(Debug)]
enum Field {
    Uid = 0,
    Id = 1,
    Name = 2,
}

pub fn main() {
    let data = [
        [String::from("X1"), String::from("A"), String::from("Car")],
        [String::from("Z1"), String::from("A"), String::from("Truck")],
        [String::from("Y1"), String::from("A"), String::from("Train")],
    ];
    
    for item in data.iter() {
        // Instead of data[2] and data[1], we use values from enum 
        // which map to 1 and 2 to make the code more readable.
        println!("Name: {}; Id: {}", item[Field::Name], item[Field::Id])
    }
}

Playground here.

The code fails:

  Compiling playground v0.0.1 (/playground)
error[E0277]: the type `[std::string::String]` cannot be indexed by `Field`
  --> src/main.rs:17:38
   |
17 |         println!("Name: {}; Id: {}", item[Field::Name], item[Field::Id])
   |                                      ^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
   |
   = help: the trait `std::slice::SliceIndex<[std::string::String]>` is not implemented for `Field`
   = note: required because of the requirements on the impl of `std::ops::Index<Field>` for `[std::string::String]`

It seems enum do not behave the way I am assuming they do. How can I achieve this behavior in Rust? Either I can improve the use of enum or there is some other construct that I can use here?

(Note: While a vec! with struct can be used to initialise data with the named fields, the purpose here is to understand how to get the above approach working when the data type available is a list.)

Edit: Corrected code sample for typo and updated the playground.

I think this makes no sens, if it's unstructured data, then the enum-shenanigans wouldn't do anything. If the data is structured, use struct.

The compiler says:

slice indices are of type usize or ranges of usize

You are trying to index with Field and although it's repr(usize), the compiler won't make an implicit conversion so you just have to make it explicit:

println!("Name: {}; Id: {}", item[Field::Name as usize], item[Field::Id as usize])
2 Likes

Whoops! That was unexpected solution. Thanks!. :sweat_smile:

On the other hand, this is pretty verbose, any less verbose alternative?

or something like

use std::ops::Index;

#[repr(usize)]
#[derive(Debug)]
enum Field {
    Uid = 0,
    Id = 1,
    Name = 2,
}

struct FieldArray([String; 3]);
impl Index<Field> for FieldArray {
    type Output = String;
    
    fn index(&self, idx: Field) -> &Self::Output { &self.0[idx as usize] }
}

pub fn main() {
    let data = [
        FieldArray([String::from("X1"), String::from("A"), String::from("Car")]),
        FieldArray([String::from("Z1"), String::from("A"), String::from("Truck")]),
        FieldArray([String::from("Y1"), String::from("A"), String::from("Train")]),
    ];
    
    for item in data.iter() {
        // Instead of data[2] and data[1], we use values from enum 
        // which map to 1 and 2 to make the code more readable.
        println!("Name: {}; Id: {}", item[Field::Name], item[Field::Id])
    }
}
3 Likes

I was looking to avoid use of struct but a clean and fine solution nonetheless, thanks!

if you don't actually ever need to construct a Field, this is by far shortest:

#[allow(non_snake_case,non_upper_case_globals)]
mod Field {
    pub const Uid: usize = 0;
    pub const Id: usize = 1;
    pub const Name: usize = 2;
}

pub fn main() {
    let data = [
        [String::from("X1"), String::from("A"), String::from("Car")],
        [String::from("Z1"), String::from("A"), String::from("Truck")],
        [String::from("Y1"), String::from("A"), String::from("Train")],
    ];
    
    for item in data.iter() {
        // Instead of data[2] and data[1], we use values from enum 
        // which map to 1 and 2 to make the code more readable.
        println!("Name: {}; Id: {}", item[Field::Name], item[Field::Id])
    }
}
4 Likes

That is indeed a clever hack! :+1:

Going to use your solution in code but selecting the answer Using enum to name list indices as solution because it address the situation in the original question. :pray:

I'm surprised that nobody has mentioned enum-map.

2 Likes