Is there a way to index an array with enums in Rust?

I want to represent an in-memory data table like this:

     | USD | EUR |
-----+-----+-----+
John | 100 | 50  |
-----+-----+-----+
Tom  | 300 | 200 |
-----+-----+-----+
Nick | 200 | 0   |
-----+-----+-----+

      

There is a well-known set of people, each of whom owns a certain currency.

And I have the following enums:

enum Person {
    John,
    Tom,
    Nick
}

enum Currency {
    USD,
    EUR
}

      

I would like to encode this data as a 2D array, and it would be great to be able to index the elements of the array not by usize

but by enum

. For example:.

data[Person::John][Currency::USD] = 100;

      

Can you do with arrays and enums in Rust? Or is there some other data structure that would serve this purpose?

I know HashMap

, but this is not exactly what I want because:

  • HashMap

    runs on the heap (making it much slower than a regular distributed array)

  • HashMap

    does not guarantee that the item exists. For example. every time I want to get something I have to expand it and handle the case None

    , which is not very convenient compared to using a regular array.

This is different from How to match enumeration values ​​with an integer? because I am not interested in converting enum to usize

; I just want a convenient way to access the elements of an array / map using an enum.

+3


source to share


3 answers


ljedrz provided a good solution. Another way to approach the problem is to use an existing enum-map box .

Add the Cargo.toml

following to yours:

[dependencies]
enum-map = "*"
enum-map-derive = "*"

      

Then in src/main.rs

:



extern crate enum_map;
#[macro_use] extern crate enum_map_derive;

#[derive(Debug, EnumMap)]
enum Person { John, Tom, Nick }

#[derive(Debug, EnumMap)]
enum Currency { USD, EUR }

use enum_map::EnumMap;
use Person::*;
use Currency::*;

fn main() {
    // Create 2D EnumMap populated with f64::default(), which is 0.0
    let mut table : EnumMap<Person, EnumMap<Currency, f64>> = EnumMap::default();

    table[John][EUR] = 15.25;

    println!("table = {:?}", table);
    println!("table[John][EUR] = {:?}", table[John][EUR]);
}

      

Output:

table = EnumMap { array: [EnumMap { array: [0, 15.25] }, EnumMap { array: [0, 0] }, EnumMap { array: [0, 0] }] }
table[John][EUR] = 15.25

      

+4


source


If you need to implement this using arrays, this is not as easy as it might seem.

To be able to contain both of these pieces of information in an array (to be able to index them), you first need to combine them into one type, for example. in structure:

struct Money([(Currency, usize); 2]);

struct PersonFinances {
    person: Person,
    money: Money
}

      

Then, if you want to be able to index the table, you need to wrap it in your own type so that you can implement for it Index

:



use std::ops::Index;

struct Table([PersonFinances; 3]);

impl Index<(Person, Currency)> for Table {
    type Output = usize;

    fn index(&self, idx: (Person, Currency)) -> &Self::Output {
        &self
        .0
        .iter()
        .find(|&pf| pf.person == idx.0) // find the given Person
        .expect("given Person not found!")
        .money
        .0
        .iter()
        .find(|&m| m.0 == idx.1)  // find the given Currency
        .expect("given Currency not found!")
        .1
    }
}

      

Then you can index the Table

pair Person

, Currency

:

table[(Tom, EUR)]

      

Rust playground link to all code

+3


source


You want HashMap

:

use std::collections::HashMap;

#[derive(PartialEq, Eq, Hash)]
enum Person {
    John,
    Tom,
    Nick
}

#[derive(PartialEq, Eq, Hash)]
enum Currency {
    USD,
    EUR
}

type Table = HashMap<Person, HashMap<Currency, f32>>;

fn main() {
    let mut table = Table::new();
    let mut currency = HashMap::<Currency, f32>::new();

    currency.insert(Currency::USD, 100_f32);
    table.insert(Person::John, currency);

    println!("{}", table[&Person::John][&Currency::USD]);
}

      

+2


source







All Articles