背景
我正在尝试在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
枚举将两个任务组合成一个模式匹配(或者如果有人有聪明的想法,可能还有别的东西)。
基本上,解决方案应该:
答案 0 :(得分:4)
load()
无法对输入参数进行模式匹配,因为根据定义,它根本没有可匹配的参数。您似乎真正想要的是通过返回类型制作load()
通用的方法 - 而且您很幸运,因为Rust实际上使这成为可能。
您需要创建一个通用特征并为您所涵盖的类型提供实施,例如i32
和String
:
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()
实施该特征,其中RegisterBank
和i32
值类型执行具体商店:
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");
}
特征,同时提供RegisterStorage
和load
。完整代码at the playground。