Bind match to C-style enum

I'd like to make this work, but can't. I want to get the values 255 and 0 from On and Off inside the match branches.

#[derive(Debug)]
#[repr(u8)]
enum Command {
    On = 255,
    Off = 0,
    Value(u8),
}

fn main() {
    for i in [Command::On, Command::Value(5), Command::Off] {
        match i {
            v @ Command::On => println!("On {:?}", v as u8),
            Command::Off => println!("Off"),
            Command::Value(v) => println!("Value {v}"),
        }
    }
}

Playground:

Do you have any ideas?

you can only cast enums with only unit variants to primitive integers. but your enum type is not a unit enum. you'll have to use discrimintant() function and use unsafe pointer casting to read the actual value. see:

That's not C style enum thus, of course, you can't.

Rather that's arbitrary enum with specified discriminant stabilized in Rust 1.66 and as notes there say:

Rust provides no language-level way to access the raw discriminant of an enum with fields. Instead, currently unsafe code must be used to inspect the discriminant of an enum with fields. Since this feature is intended for use with cross-language FFI where unsafe code is already necessary, this should hopefully not be too much of an extra burden.

You can look on the appropriate RFC for details.

Is it actually guaranteed or not, though? RFC talks about use of pointers, very explicitly without use of discriminant() and I don't know of any official doc which tells us that discriminant() is guaranteed to return the same value.

btw, the #[repr(u8)] probably doesn't do what you might think it would do. it specifies the type of the discriminant, so your enum will be size of 2 bytes, not 1 byte.

Yes, but it's also critically important because without it one can not use pointer casts and be sure that they would work as expected.

Thanks for your replies.

To clarify a bit, I'm fine with it taking 2 bytes. I'm not doing any FFI, but rather I'm making a definition for clap derive. The interface I want to achieve is:

$ cli on
$ cli off
$ cli value 5

and I want to assign constant values to On and Off, that would be passed on.

Currently I have something like this (yes, I'm omitting parts of code for clarity):

#[derive(Subcommand)]
enum Command {
    On,
    Off,
    Value(u8),
}

match Cli::parse().command {
    Command::On => set_power(1),
    Command::Off => set_power(0),
    Command::Value(v) => set_value(v),
}

and I wanted to reduce both lines with On and Off into single line.

Ideally it would look like this (warning: invalid code ahead):

#[derive(Subcommand)]
enum Command {
    On = 1,
    Off = 0,
    Value(u8),
}

match Cli::parse().command {
    v @ Command::On | v @ Command::Off => set_power(v as u8),
    Command::Value(v) => set_value(v),
}

I just wouldn't do that if I were you. Your working code is fine as is and clearly legible. Why does the relation between on/off and 1/0 have to be encoded in the enum itself? What benefit does that give you?

Come to think of it why is Command even an enum and not just struct Command(u8)?

You can even add helper functions to get a on and off command.

struct Command(u8)

impl Command {
    fn on() -> Self {
        Self(255)
    }

    fn off() -> Self {
       Self(0)
    }
}
1 Like

I found something satisfying:

#[derive(Subcommand)]
enum Power {
    On = 0,
    Off = 1,
}
#[derive(Subcommand)]
enum Command {
    #[command(flatten)]
    Power(Power),
    Value(u8),
}

match Cli::parse().command {
    Command::Power(power) => set_power(power as u8),
    Command::Value(v) => set_value(v),
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.