有没有办法在 Rust 中通过枚举索引数组?



我想在内存中表示一个数据表,如下所示:

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

有一群已知的人,他们每个人都拥有一些货币。

我有以下枚举:

enum Person {
John,
Tom,
Nick
}
enum Currency {
USD,
EUR
}

我想将此数据编码为 2D 数组,并且能够不按usize而是按enum索引数组元素会很酷。 例如:

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

是否可以在 Rust 中使用数组和枚举?或者是否有任何其他数据结构可以为此服务?

我知道HashMap,但这并不完全是我想要的,因为:

  • HashMap在堆上工作(这使得它比常规堆栈分配数组慢得多(

  • HashMap不能保证该项目存在。 例如,每次我想得到一些东西时,我都必须打开它并处理None情况,与使用普通数组相比,这不是很方便。

这与如何将枚举值与整数匹配不同? 因为我对将枚举转换为usize不感兴趣;我只是想要一种通过枚举访问数组/映射项目的便捷方法。

ljedrz 提供了一个很好的解决方案。 解决这个问题的另一种方法是使用现有的 crate 枚举映射。

将以下内容添加到您的Cargo.toml

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

然后,在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]);
}

输出:

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

如果您需要使用数组实现这一点,这并不像看起来那么简单。

为了能够在数组中包含这两个信息(以便能够按它们进行索引(,您首先需要将它们组合成一个类型,例如在结构中:

struct Money([(Currency, usize); 2]);
struct PersonFinances {
person: Person,
money: Money
}

然后,如果您希望能够为表编制索引,则需要将其包装在您自己的类型中,以便您可以为其实现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
}
}

然后,您可以通过PersonCurrency对来索引Table

table[(Tom, EUR)]

锈游乐场链接到整个代码

这并不像想象的那么难。 我很欣赏你说你"对将枚举转换为 usize 不感兴趣",但可能是人们误解你的意思是你想不惜一切代价避免这种情况。 您在这里要做的就是:

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

只要data真的是一个 2D 数组,一切都会在没有警告或错误的情况下编译,并且会按预期工作。

实际上,您可能需要在第一个元素上添加"= 0"来声明 Person,如下所示:

enum Person { John = 0, Dave, Dan, Will }

我喜欢使用这个秘籍:

enum Person { John = 0, Dave, SIZE }
enum Currency { USD = 0, CAD, SIZE }
let mut my2d: [[f32; Person::SIZE as usize]; Currency::SIZE as usize] = [[0., 0.], [0., 0.]];
my2d[Person::John as usize][Currency::USD as usize] = 5.4;
println!("$$: {}", my2d[Person::John as usize][Currency::USD as usize]);

你想要一个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]);
}

最新更新