匹配 Rust 中枚举中的非变量值

Match non variant values in enum in Rust

提问人:Roman Liutko 提问时间:10/8/2023 最后编辑:BiagioFRoman Liutko 更新时间:10/8/2023 访问量:81

问:

假设我有以下枚举 - 混合 c 样式赋值和变体枚举:

enum Command {
    Control(ControlSubcommand) = 0x00,
    Temperature = 0x02,
    Voltage = 0x04,
    ...
}

enum ControlSubcommand {
     Status = 0x00,
     DeviceType = 0x01,
     ....
}

然后,当我尝试将给定命令解构为单个值时,我将其传递给函数。对于直接分配值(温度、电压等)的 c 样式命令,我想获取它的相应值 - 所以0x02、0x04等。

对于“变体”命令(Control),我想获取它的值和子命令 - 因此0x00 0x00,0x01等。

我正在尝试编写以下匹配结构来做到这一点:

match command {
    Command::Control(subcommand) => {
        println!("control command with subcommand {}", subcommand as u32);
    }
    simple => {
        println!("simple command {}", simple as u32);
    }
}

但问题是,从技术上讲,它仍然是一个非原始类型,所以 cast 不能使用它。尽管之前已经处理过变体情况并且不会出现在 中,但 Rust 仍然不允许我执行投射。simpleCommandassimple

是否可以在不更改枚举布局的情况下以某种方式修复此代码?

  • 使用 if-let 语法无济于事。
  • 简单的投射不起作用as
Rust 枚举铸造 模式 匹配

评论

1赞 BiagioF 10/8/2023
impl 是一种选择吗?From<Command> for u32
0赞 Peter Hall 10/8/2023
“不改变枚举布局?”-- 你指的是内存中的布局还是类型定义?
0赞 Chayim Friedman 10/8/2023
最简单的方法是简单地匹配每个元素。
0赞 Roman Liutko 10/8/2023
感谢您的回答!@BiagioF 我刚刚开始使用 Rust,所以如果我遗漏了一些东西,我很抱歉 - 但是 trait 实现将如何区分类型?我怀疑我最终需要实现几乎相同的匹配结构。
1赞 Chayim Friedman 10/8/2023
只管去做。当然,你可以把它封装在一个函数中。这比在项目中使用不安全代码的替代方案要好。编译器也会对其进行优化。如果你真的需要,你可以让一个宏生成它,或者有几十个变体或代码不断变化。虽然老实说,即使在这些情况下,我也只是手写它,除非你有很多这样的枚举。

答:

2赞 Chayim Friedman 10/8/2023 #1

没有安全的方法可以做到这一点。如果枚举是(必须是枚举才能定义判别器)。#[repr(integer)]

但是您可以使用不安全代码读取判别器,如文档中所述

#[repr(u32)]
enum Command {
    Control(ControlSubcommand) = 0x00,
    Temperature = 0x02,
    Voltage = 0x04,
    // ...
}

enum ControlSubcommand {
    Status = 0x00,
    DeviceType = 0x01,
    //  ....
}

fn get_discriminant(v: Command) -> u32 {
    match v {
        Command::Control(v) => v as u32,
        // SAFETY: Because of `#[repr(u32)]`, the first field is the `u32` discriminant.
        _ => unsafe { *(&v as *const Command as *const u32) },
    }
}

评论

0赞 cafce25 10/8/2023
难道不是比 2 倍铸造更可取吗?(实际上不确定)*<*const Command>::cast(&v)as
0赞 Chayim Friedman 10/8/2023
@cafce25 这是主观的(你的变体充满了印记)。虽然老实说,我没有考虑过这个选项。
0赞 rodrigo 10/9/2023
为了增加自行车棚,现在我更喜欢.(&v as *const Command).cast::<u32>().read()