使用模式匹配在struct字段之间进行选择

时间:2015-08-19 22:46:34

标签: rust

背景

我正在尝试在Rust中实现一个简单的虚拟机。目前,我正在开发“RegisterBank”,它支持字符串和整数的空间。 RegisterBank 结构体如下所示:

pub struct RegisterBank {
    int_registers: Vec<i32>,
    str_registers: Vec<String>,
}

所以它只是两个向量的简单集合。

以前的工作:

当我尝试实现“加载”和“存储”功能时,可以选择两个单独的功能

pub fn load_int(...) { ... }
pub fn load_str(...) { ... }

和模式匹配(无论如何我都想学习)

pub fn load(self, register: SomeMatchableType) {
    match register { ... }
}

因为对两个非常相似的任务有一个功能似乎很好,我试过这样的事情:

enum OperandType {
    Number(i32),
    Word(String),
}

然后有一个像pub fn load(self, register: OperandType)这样的函数匹配register并返回一个String(或&amp; str,无论如何)或一个整数,具体取决于它的操作数类型。

问题: 目前实现是停留在两个单独的函数(对于i32和String),这是正常的。由于我已设法为fn store(&mut self, register: usize, value: OperandType)执行此操作,因此fn load(self, register: ???)也应该可以执行此操作。我最大的问题是设计这样一个功能,根据OperandType枚举将两个任务组合成一个模式匹配(或者如果有人有聪明的想法,可能还有别的东西)。

基本上,解决方案应该:

  1. 根据输入参数
  2. 决定选择哪个寄存器(int_registers或str_register)
  3. 获取注册表的内容
  4. 退货

1 个答案:

答案 0 :(得分:4)

load()无法对输入参数进行模式匹配,因为根据定义,它根本没有可匹配的参数。您似乎真正想要的是通过返回类型制作load()通用的方法 - 而且您很幸运,因为Rust实际上使这成为可能。

您需要创建一个通用特征并为您所涵盖的类型提供实施,例如i32String

trait LoadFromRegister<T> {
    fn load(&self, register: usize) -> T;
}

impl LoadFromRegister<i32> for RegisterBank {
    fn load(&self, register: usize) -> i32 {
        self.int_registers[register]
    }
}

impl LoadFromRegister<String> for RegisterBank {
    fn load(&self, register: usize) -> String {
        self.str_registers[register].clone()
    }
}
可以通过在上下文中提供返回类型或使用starfish运算符显式调用

load

let intreg: i32 = rb.load(2);  // or, let intreg = rb.load::<i32>(2);
let strreg: String = rb.load(2);

类似的技术可以用来摆脱笨重的OperandType包装器。定义提供StoreToRegister的{​​{1}}特征,并为store()实施该特征,其中RegisterBanki32值类型执行具体商店:

String

这提供了与参数重载最接近的Rust等价物,允许trait StoreToRegister<T> { fn store(&mut self, register: usize, value: T); } impl StoreToRegister<i32> for RegisterBank { fn store(&mut self, register: usize, n: i32) { self.int_registers[register] = n; } } impl StoreToRegister<String> for RegisterBank { fn store(&mut self, register: usize, s: String) { self.str_registers[register] = s; } } 看起来像这样:

main()

这两个特征可以合并为一个fn main() { let mut rb = RegisterBank::new(); rb.store(2, 5); rb.store(2, "foo".to_owned()); let intreg: i32 = rb.load(2); let strreg: String = rb.load(2); assert!(intreg == 5); assert!(strreg == "foo"); } 特征,同时提供RegisterStorageload。完整代码at the playground