在Rust中,有没有一种方法可以通过枚举类型来对数组进行索引?

28

我希望能够像以下这样在内存中表示数据表:

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

有一组已知的人,每个人都拥有一些货币。

我有以下枚举:

enum Person {
    John,
    Tom,
    Nick
}

enum Currency {
    USD,
    EUR
}

我希望将这些数据编码为二维数组,并且能够通过enum而不是usize来索引数组元素,这将会很棒。例如:
data[Person::John][Currency::USD] = 100;

在 Rust 中,是否可以使用数组和枚举来实现这个功能?或者是否有其他数据结构可以用于此目的?

我知道 HashMap,但它并不完全符合我的要求,因为:

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

  • HashMap 不能保证项目存在。例如,每次我想获取某些内容时,都必须将其解包并处理 None 情况,与使用普通数组相比不太方便。

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


为什么不将Person和Currency作为Trait使用,并在John、Tom、Nick和USD、EUR上实现这些Trait呢? - Boiethios
听起来你想要一个从名称-货币对到值的关联数组。你尝试过什么吗?HashMap - E net4
为什么不将Person和Currency作为Trait,并在John、Tom、Nick和USD、EUR上实现这些Trait呢?我不确定我是否理解了这个想法,但无论如何,我想要USD和EUR的枚举类型,因为在我的应用程序的其他地方,我需要它们作为枚举类型。听起来你想要一个从名称-货币对到值的关联数组。你尝试过什么吗?谢谢回复。我知道HashMap,但它并不完全符合我的需求。HashMap在堆上工作。 - Sergey Potapov
可能是重复的问题:如何将枚举值与整数匹配? - the8472
4个回答

20

ljedrz提供了一个好的解决方案。 另一种方法是使用现有的crate enum-map来处理这个问题。

将以下内容添加到你的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

9
如果您需要使用数组来实现此操作,那么这并不像看起来的那么简单。
为了能够在数组中同时包含这两个信息(以便能够通过它们进行索引),您首先需要将它们组合成一个单一的类型,例如结构体:
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)]

Rust示例链接,包含整个代码


2
非常低效。索引的整个目的是为了实现恒定(或接近恒定)的访问时间。这对于示例来说还可以,但对于实际情况来说并不好。 - Hutch Moore

1

这并不像人们所说的那样难。我很感激你说你“不想将枚举转换为usize”,但可能有些人误解了你的意思,认为你要尽一切努力避免这种情况。在这里,你只需要:

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

只要data确实是一个二维数组,所有内容都会编译通过且正常工作,不会出现警告或错误。
实际上,你可能需要在第一个元素上添加“= 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]);

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

4
谢谢回复。我知道HashMap,但它并不完全符合我的要求,因为:
  • HashMap 在堆上运行(这使它比常规栈分配的数组慢得多)
  • HashMap 不能保证项目存在。例如,每次我想“获取”某些东西时,我都必须将其拆包并处理“None”情况,与普通数组相比不太方便。
- Sergey Potapov
@SergeyPotapov 所以,正如其他答案中所说,您必须实现自己的类型。 - Boiethios

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接