Roman Liutko
Roman Liutko

Reputation: 21

Match non variant values in enum in Rust

Let's say I have the following enums - mixing c-style assigned values and variant enums:

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

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

I then pass it to a function when I try to deconstruct the given command to individual values. For a c-style command with a value directly assigned (Temperature, Voltage and so on) I want to get it's corresponding value - so 0x02, 0x04 and so on.

For the "variant" command (Control) I want to get the it's value and subcommand - so 0x00 with 0x00, 0x01 and so on.

I'm trying to write the following match construct to do so:

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

But the problem is that technically simple is still a Command which is a non-primitive type, so as cast does not work with it. Even though the variant case was already handled before and will not appear in simple, Rust still disallows me from performing a cast.

Is it possible to somehow fix this code without changing the enum layout?

Upvotes: 0

Views: 125

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 71310

There is no safe way to do that. If the enum is #[repr(integer)] (which it must be to be able to define the discriminants).

But you can read the discriminant using unsafe code, as explained in the documentation:

#[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) },
    }
}

Upvotes: 2

Related Questions