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 caseNone
, 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.
source to share
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
source to share
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)]
source to share
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]);
}
source to share