Bindings for enumerations

Here we describe how Crubit maps enumerations: a Rust unit-only enum or a C++ enum.

Rust bindings for C++ enums

For the following C++ header:

enum Color {
  kRed,
  kBlue,
  kGreen,
};

Crubit will generate the following bindings:

#[repr(transparent)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
pub struct Color(u32);
impl Color {
    pub const kRed: Color = Color(0);
    pub const kBlue: Color = Color(1);
    pub const kGreen: Color = Color(2);
}

impl From<u32> for Color {
    fn from(value: u32) -> Color {
        Color(value)
    }
}

impl From<Color> for u32 {
    fn from(value: Color) -> u32 {
        value.0
    }
}

A C++ enum is translated into a set of const items in Rust, because this most accurately represents the fact that C++ enumerations are non-exhaustive (i.e. in C++ any in-range value can be cast to the enumeration, even if it wasn‘t listed in the enum declaration). In other words, C++ behavior doesn’t match Rust enums where “a discriminant in an enum not included in the type definition” is listed as a potential source of Undefined Behavior.

TODO: Consider allowing C++ enums to be marked with an attribute (e.g. [[crubit::exhaustive]]?) and translate them to a Rust enum.

C++ bindings for Rust unit-only enums

For the following Rust type:

pub enum Color {
    Red,
    Green,
    Blue,
}

Crubit will generate the following bindings:

struct alignas(1) Color final {
    public:
        // The Rust type has no `Default` impl.
        Color() = delete;

        // The Rust type is not `Copy`.
        Color(const Color&) = delete;
        Color& operator=(const Color&) = delete;

        // All non-`Drop` Rust types are trivially-movable.
        Color(Color&&) = default;
        Color& operator=(Color&&) = delete;

        // The Rust type has no `Drop` impl,
        // nor requires custom drop glue.
        ~Color() = default;
    private:
        ...
};

Note that the generated C++ bindings are currently opaque (b/259984090 and b/280861833 track adding more idiomatic bindings for enumerations). In particular, the C++ side doesn't currently have any direct visibility into the discriminant the Rust enum. Nevertheless, the bindings should cover methods and trait of the Rust enum - for example:

  • mapping static methods from Rust to non-member methods in C++
  • mapping Default trait impl from Rust to the default constructor in C++ (this bullet item is WIP - see b/258249980)