Union of two non-overlapping enums

I have two non-overlapping enums that are passed to me from C.
Is there a way to write a enum, encompassing both, which is of the same size as the original enum (so that I could use all of them in C interface)?

Here is the example:

#[repr(u8)]
#[rustfmt::skip]
pub enum low_level_message_type_enum {                                       
    initiate_disconnection = 0x0,
    confirm_disconnection  = 0x1,
    continuous_flow        = 0x2,
}

#[repr(u8)]
#[rustfmt::skip]
pub enum match_client_message_types_enum {
    connection_request                 = 0x40,
    get_startup_info                   = 0x41,
    join_match                         = 0x42,
    client_player_update               = 0x43,
    client_player_commit_suicide       = 0x44,
    time_synchronization_request       = 0x45,
    time_synchronization_confirmation  = 0x46,
    bullets_info_request               = 0x47,
    team_bases_initialize_info         = 0x48,
    force_finish_match                 = 0x49,
    world_synchronization_confirmation = 0x4A,
    match_client_invalid_message_type  = 0x7F,
}
    
#[repr(u8)]
pub enum both {
    Low(low_level_message_type_enum),
    High(match_client_message_types_enum),
}

fn main() {
    assert_eq!(std::mem::size_of::<low_level_message_type_enum>(), 1);
    assert_eq!(std::mem::size_of::<match_client_message_types_enum>(), 1);
    assert_eq!(std::mem::size_of::<both>(), 1); // <- fails
}

No, this layout optimization is not guaranteed.

You'll need to copy-paste the variants into a single enum ;(

Can I use union for enum both and use it in C interface?

I should be able to use bytemuck for generating safe conversions.

#[repr(C)]
#[derive(Copy, Clone)]
pub union both {
    l: low_level_message_type_enum,
    h: match_client_message_types_enum,
}

Using union will work, but it may require unsafe code.

2 Likes

I wonder, though, whether this will work if both partial enums are enums and not unions themselves. I.e. do #[repr(u8)] enums work in a #[repr(C)] union?

I personally would travel the safe route and introduce a joint enum for all values and represent the enums from C separately (possible as #[repr(C)]) and then implement From<...> for MyEnum from both those C-enums.

It works as long as there is no overlap in the discriminants. If there's overlap, then you can't tell which enum it came from for the cases where it overlaps.

And not only is it not guaranteed, it's not even implemented, so one can't even const_assert it or anything.

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.