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

Posted

技术标签:

【中文标题】有没有办法在 Rust 中通过枚举来索引数组?【英文标题】:Is there a way to index an array by enums in Rust? 【发布时间】:2017-12-07 14:55:39 【问题描述】:

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

     | 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的情况,这与使用普通数组相比不是很方便。

这与How do I match enum values with an integer? 不同,因为我对将枚举转换为usize 不感兴趣;我只是想要一种方便的方法来通过枚举访问数组/映射项。

【问题讨论】:

为什么不使用 Person 和 Currency 作为 Trait 并在 John、Tom、Nick 和 USD、EUR 上实现这些特征? 听起来你想要一个从名称-货币对到一个值的关联数组。你尝试过什么吗? HashMap? > 为什么不使用 Person 和 Currency 作为 Trait 并在 John、Tom、Nick 和 USD、EUR 上实现这些特征?我不确定我是否有这个想法,但无论如何我想要 USD 和 EUR 枚举,因为在我的应用程序的其他地方我需要它们作为枚举。 > 听起来您想要一个从名称-货币对到值的关联数组。你尝试过什么吗?谢谢你的答复。我知道 HashMap 但这并不是我所需要的。 HashMap 在堆上工作。 How do I match enum values with an integer?的可能重复 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

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

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 playground link to the whole code

【讨论】:

【参考方案3】:

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

【讨论】:

您好,感谢您的回复。我知道 HashMap,但这并不是我想要的,因为: - HashMap 在堆上工作(这使得它比常规堆栈分配的数组慢得多) - HashMap 不能保证项目存在。例如。每次我想get 的东西我必须打开它并处理None 的情况,这与普通数组相比不是很方便。 @SergeyPotapov 所以,正如另一个答案中所说,您必须实现自己的类型。

以上是关于有没有办法在 Rust 中通过枚举来索引数组?的主要内容,如果未能解决你的问题,请参考以下文章

在 Rust 中,有没有办法遍历枚举的值?

有没有办法在函数调用中通过引用传递和通过值显式传递?

有没有办法在 devexpress TreeListControl 中通过拖放重新排列节点时预览元素的位置?

在 C# 中通过 varchar2 使用 PL/SQL assoc 数组索引

有没有办法使用 AnroidUIAutomator 在 Appium 中通过部分资源 ID 查找 Android 元素?

Rust学习教程17 - 枚举enum