【问题标题】:How to find the number of arguments of an enum variant?如何找到枚举变体的参数数量?
【发布时间】:2019-01-27 23:22:03
【问题描述】:

我有一个枚举,它代表 8080 处理器上所有可能的指令。一条指令可以是 1、2 或 3 个字节长,这取决于它是否有与之相关的信息以及有多少。例如:

#[allow(non_camel_case_types)]
enum Instruction {
    None,
    NOP,
    LXI_B_D16(u8, u8),
    STAX_B,
    INX_B,
    MVI_B_D8(u8),
    MVI_C_D8(u8),
    RRC,
    LXI_D_D16(u8, u8),
    MVI_D_D8(u8),
    RAL,
    DCR_E,
    MVI_E_D8(u8),
    LXI_H_D16(u8, u8),
    SHLD(u16),
    LHLD(u16),
    // ...
}

在为指令分配内存地址时,我在二进制文件中逐条指令迭代,使用每条指令的长度来确保我的循环不会在指令中途着陆并给我垃圾。我用一个巨大的匹配表达式来做到这一点,它返回一个包含正确指令及其长度的元组:

match value {
    0x00 => (Instruction::NOP, 1),
    0x01 => (Instruction::LXI_B_D16(d1, d2), 3),
    0x02 => (Instruction::STAX_B, 1),
    0x05 => (Instruction::DCR_B, 1),
    0x06 => (Instruction::MVI_B_D8(d1), 2),
    0x07 => (Instruction::RLC, 1),
    0x0e => (Instruction::MVI_C_D8(d1), 2),
    0x0f => (Instruction::RRC, 1),
    0x11 => (Instruction::LXI_D_D16(d1, d2), 3),
    0x19 => (Instruction::DAD_D, 1),
    // ...
}

这很难看,但我不想将这个长度数字与类型相关联,因为它真的只在我解析文件时才重要。

似乎我应该能够从变体的形状推断出指令的长度。不带参数的长度为 1,带一个 u8 参数的长度为 2,带一个 u16 或两个 u8 参数的长度为 3。

我无法弄清楚如何以编程方式获得这种形状。例如,我不能像数组或向量那样调用len()

我不认为这是 How to get the number of elements in an enum as a constant value? 的副本,因为我不是在寻找一种方法来获取枚举中的变体数量,而是任何单个变体的参数数量.

【问题讨论】:

  • 我不认为这是可能的,我也想知道你在用这些信息做什么。你不应该需要这个。
  • 似乎潜在的问题更多是关于消除重复。在 Rust 中做到这一点的一种方法是使用宏。您可以使用宏在生成大匹配表达式的同时生成指令枚举。
  • @stargateur 一个 8080 ROM(在我的例子中是 Space Invaders)作为单个 u8 数组读入我的程序。每条指令的长度为 1、2 或 3 个 u8:第一个 u8 表示指令类型,其余字节是相关数据。例如,一个 JMP 命令有一个关联的 u16 内存地址来标记跳转位置。当我解析这个数组时,我按指令字节进行迭代,所以我需要知道指令的长度才能知道指针增加多少。这可能是一种非常幼稚的做法,我不确定!

标签: enums rust


【解决方案1】:

如cmets中所说,你可以写一个宏来为你写代码。

为了懒惰,我简化了枚举定义以始终要求括号。这不是必需的,它只是简化了我的工作。

一旦枚举被宏解析,我们就可以生成一个具有匹配每个变体的函数的 impl 块。我们将每个变体的参数传递给为我们执行计数的内部宏。此函数返回每个变体中的元素数量:

macro_rules! instructions {
    (enum $ename:ident {
        $($vname:ident ( $($vty: ty),* )),*
    }) => {
        enum $ename {
            $($vname ( $($vty),* )),*
        }

        impl $ename {
            fn len(&self) -> usize {
                match self {
                    $($ename::$vname(..) => instructions!(@count ($($vty),*))),*
                }
            }
        }
    };

    (@count ()) => (0);
    (@count ($a:ty)) => (1);
    (@count ($a:ty, $b:ty)) => (2);
    (@count ($a:ty, $b:ty, $c:ty)) => (3);
}

instructions! {
    enum Instruction {
        None(),
        One(u8),
        Two(u8, u8),
        Three(u8, u8, u8)
    }
}

fn main() {
    println!("{}", Instruction::None().len());
    println!("{}", Instruction::One(1).len());
    println!("{}", Instruction::Two(1,2).len());
    println!("{}", Instruction::Three(1,2,3).len());

}

您也可以编写具有相同功能的a custom derive macro

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-04
    • 1970-01-01
    • 2017-05-29
    • 2020-11-23
    • 2012-04-16
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    相关资源
    最近更新 更多