【问题标题】:Create an array with enum element size创建一个具有枚举元素大小的数组
【发布时间】:2015-01-09 17:19:58
【问题描述】:

我想创建一个大小由枚举中的元素指定的数组,如下所示:

pub use self::Register::*;

enum Register {
    Ip,
    Sp,
    NumRegs,
}

struct State {
    val: int,
    regs: [int; NumRegs as int],
    running: bool,
}

但我明白了:

src/main.rs:19:11: 19:32 error: expected constant expr for array length: non-constant path in constant expr
src/main.rs:19     regs: [int; NumRegs as int],

我尝试过使用as int 以及其他解决方案,并在谷歌上搜索了一段时间,但没有找到解决方案。顺便说一句,Rust 中的这种形式很糟糕吗?

【问题讨论】:

  • enums 在语义上并不对应于数值,就像您来自 C 语言时所期望的那样。您能否详细解释一下您真正想要的代码?

标签: arrays enums rust


【解决方案1】:

目前,我不认为 Rust 能够看到 Enum::Variant 实际上是一个常量(我实际上也不知道它是),所以它不能用作数组长度。不仅如此,我会说添加一个实际上并不打算使用的枚举变体很奇怪。

对于我如何看待您的问题,我可能会尝试将您的寄存器表示为结构:

struct Registers {
    ip: u8,
    sp: u8,
}

struct State {
    val: u8,
    regs: Registers,
    running: bool,
}

fn main() {
    let s = State {
        val: 0,
        regs: Registers { ip: 0, sp: 0 },
        running: false,
    };
}

编辑

如果您想按姓名注册,我们直接这样做怎么样:

struct Registers {
    ip: u8,
    sp: u8,
}

impl Registers {
    fn by_name(&self, name: &str) -> u8 {
        match name {
            "ip" => self.ip,
            "sp" => self.sp,
            _ => panic!("Unknown register '{}'", name),
        }
    }

    fn by_name_mut(&mut self, name: &str) -> &mut u8 {
        match name {
            "ip" => &mut self.ip,
            "sp" => &mut self.sp,
            _ => panic!("Unknown register '{}'", name),
        }
    }
}

fn main() {
    let mut r = Registers { ip: 0, sp: 0 };

    println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
    *r.by_name_mut("ip") += 1;
    println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
}

虽然panic! 确实很丑……但我宁愿为此目的使用枚举。让我们同时做枚举和字符串:

use std::str::FromStr;

#[derive(Debug,Copy,Clone,PartialEq)]
enum Register {
    Ip,
    Sp,
}

impl FromStr for Register {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, ()> {
        match s {
            "ip" => Ok(Register::Ip),
            "sp" => Ok(Register::Sp),
            _ => Err(()),
        }
    }
}

struct Registers {
    ip: u8,
    sp: u8,
}

impl Registers {
    fn by_name(&self, name: Register) -> u8 {
        match name {
            Register::Ip => self.ip,
            Register::Sp => self.sp,
        }
    }

    fn by_name_mut(&mut self, name: Register) -> &mut u8 {
        match name {
            Register::Ip => &mut self.ip,
            Register::Sp => &mut self.sp,
        }
    }
}

fn main() {
    let mut rs = Registers { ip: 0, sp: 0 };

    let r: Register = "ip".parse().unwrap();

    println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
    *rs.by_name_mut(r) += 1;
    println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
}

现在我们在将字符串转换为 Register 类型之间有一个很好的清晰分离,这意味着我们的代码只有一部分必须处理无效的寄存器名称(对 unwrap 的调用)。

【讨论】:

  • 嗨!我正在尝试创建一个可由枚举索引的寄存器数组,以便我可以通过字符串进行简单的反向查找。基本上我不想要任何关于我如何寻址寄存器的特别之处。不确定这是否有意义。
  • 如果您愿意,可以查看 github.com/tekknolagi/carp/tree/master/src/。类似的设计,但也许有更惯用的设计。
  • @tekknolagi,我已经更新了我的答案,以展示我将如何使用字符串来识别并提供对特定寄存器的访问权限。
  • 感谢您抽出宝贵时间回复。如果我要制作一个简单的虚拟机,有没有更清洁的方法呢?这对我来说似乎有点笨拙。暂时忽略 Carp —— 你会用什么方式创建一个小的字节码解释器(至少是它的这一部分)?
  • 我在口译领域没有任何经验。也许你可以扩展你觉得笨重的东西?我看到的最大的一个是by_name 和by_name_mut 之间的重复,但宏可能会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-24
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多