C++ bindings for Rust enums
A Rust enum is mapped to an opaque C++ type.
If an enum is not C++ movable, then it will be passed
by-value via the rs::Movable wrapper type.
See Movable Types.
Example
Given the following Rust crate:
#[derive(Copy, Clone, Default)]
pub enum Color {
#[default]
Red,
Blue,
Green,
}
Crubit will generate the following bindings:
struct CRUBIT_INTERNAL_RUST_TYPE(":: example_crate_golden :: Color") alignas(1)
[[clang::trivial_abi]] Color final {
public:
// Default::default
Color();
static constexpr Color MakeRed();
static constexpr Color MakeBlue();
static constexpr Color MakeGreen();
// No custom `Drop` impl and no custom "drop glue" required
~Color() = default;
Color(Color&&) = default;
Color& operator=(Color&&) = default;
// Rust types that are `Copy` get trivial, `default` C++ copy constructor and
// assignment operator.
Color(const Color&) = default;
Color& operator=(const Color&) = default;
Color(::crubit::UnsafeRelocateTag, Color&& value);
private:
// Field type has been replaced with a blob of bytes: No support for bindings
// of individual non-repr(C) `enum`s
::std::array<unsigned char, 1> __opaque_blob_of_bytes;
private:
struct PrivateBytesTag {};
constexpr Color(PrivateBytesTag, ::std::array<unsigned char, 1> bytes)
: __opaque_blob_of_bytes(bytes) {}
private:
static void __crubit_field_offset_assertions();
};
Why isn’t it a C++ enum?
A repr(i32) or fieldless repr(C) enum is very similar to a C++ enum.
However, Rust enums are exhaustive: any value not explicitly listed in the
enum declaration does not exist, and it is undefined
behavior
to attempt to create one.
C++ enums, in contrast, are “non-exhaustive”: a C++ enum can have any
value supported by the underlying type, even one not listed in the enumerators.
For example, if the above example were a C++ enum, static_cast<Color>(42)
would be a valid instance of Color, even though neither Red, Blue, nor
Green have that value.
In order to prevent invalid Rust values from being produced by C++, a C++ enum
cannot be used to represent a Rust enum. Instead, the C++ bindings are a
struct, even for fieldless enums.
C++ movable
If an enum is not C++ movable, its C++ move constructor
and move assignment operator will be = deleted, and it will be passed by-value
via the rs::Movable wrapper type. See
Movable Types.
Enums with payload
Each variant of a Rust enum may contain an additional payload (a tuple or a
struct). C++ bindings for Rust enums provide the following ways of working
with an enum payload:
-
Constructing an
enumvariant with the given payload by calling astaticMake<variant name>method (one such method is injected for each ofenumvariants). The following bugs track future work in this area:- b/487357254: Constructing variants with a struct payload
-
TODO(b/262737383): Matching
enumvariants and inspecting their payload.
Example
Given the following Rust crate:
#[derive(Copy, Clone)]
pub enum Color {
/// A completely transparent color (no payload)
Transparent,
/// A grayscale value from 0 to 255
Grayscale(u8),
/// Red, Green, and Blue values from 0 to 255
Rgb(u8, u8, u8),
}
Crubit will generate the following bindings:
struct CRUBIT_INTERNAL_RUST_TYPE(":: example_crate_golden :: Color") alignas(1)
[[clang::trivial_abi]] Color final {
public:
// `example_crate_golden::Color` doesn't implement the `Default` trait
Color() = delete;
// A completely transparent color (no payload)
static constexpr Color MakeTransparent();
// A grayscale value from 0 to 255
static ::example_crate::Color MakeGrayscale(::std::uint8_t __param_0);
// Red, Green, and Blue values from 0 to 255
static ::example_crate::Color MakeRgb(::std::uint8_t __param_0,
::std::uint8_t __param_1,
::std::uint8_t __param_2);
// No custom `Drop` impl and no custom "drop glue" required
~Color() = default;
Color(Color&&) = default;
Color& operator=(Color&&) = default;
// Rust types that are `Copy` get trivial, `default` C++ copy constructor and
// assignment operator.
Color(const Color&) = default;
Color& operator=(const Color&) = default;
Color(::crubit::UnsafeRelocateTag, Color&& value);
private:
// Field type has been replaced with a blob of bytes: No support for bindings
// of individual non-repr(C) `enum`s
::std::array<unsigned char, 4> __opaque_blob_of_bytes;
private:
struct PrivateBytesTag {};
constexpr Color(PrivateBytesTag, ::std::array<unsigned char, 4> bytes)
: __opaque_blob_of_bytes(bytes) {}
private:
static void __crubit_field_offset_assertions();
};