rust - 使用模式匹配在结构字段之间进行选择

标签 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) 的函数它匹配寄存器并返回一个字符串(或&str,等等)或一个整数,具体取决于其操作数类型。

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

基本上,解决方案应该这样做:

  1. 根据输入参数决定选择哪个寄存器(int_registers或str_register)
  2. 获取寄存器的内容
  3. 返回

最佳答案

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()
    }
}

load 可以通过在上下文中提供返回类型或显式使用海星运算符来调用:

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

类似的技术可以用来摆脱笨重的OperandType包装器。定义一个提供 store()StoreToRegister 特征,并使用 i32String< 为 RegisterBank 实现它 执行具体存储的值类型:

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;
    }
}

这提供了与参数重载最接近的 Rust 等效项,允许 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特征。完整代码at the playground .

关于rust - 使用模式匹配在结构字段之间进行选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32106850/

相关文章:

windows - 无法在 Windows 上使用 Rust 和 GTK 运行 pkg-config

不满足 Rust 特征边界。中间分配是必要的,或者删除 Trait 实现

rust - cargo sqlx prepare 生成 stub 文件——即使启用了 'offline' 功能

c - 我想从 Rust 语言调用 C 库 "mysql.h"

pointers - 如何将可以为null的原始指针转换为Option?

iterator - 有条件地从 flat_map 返回空迭代器

javascript - js导入找不到wasm文件

rust - 我如何在 Polars Rust 中的日期列的年份或工作日进行分组

multithreading - 如何启动和停止工作线程

rust - 检查 Rust 是否正在运行测试构建