Add enum option constified_enum + rustified_enum with conversions · Issue #2646 · rust-lang/rust-bindgen (original) (raw)

All ways of interop with C enums unfortunately have some downsides. constified-enum, constified-enum-module, newtype-enum, and bitfield-enum can't be matched exhaustively: using the below demo, for example:

match some_foo { foo_one => println!("1"), foo_too => println!("2"), foo_three => println!("3"), _ => unimplemented!() }

If a new variant is added to the enum, it gets swallowed with the _. Or a variant may have accidentally be emitted in the first place.

rustified_enum and rustified-non-exhaustive-enum provide more ergonomic solutions and are easier to match, but they don't have good handling for if C provides value not covered by a variant - which is allowed in C. This leads to bugs that can be impossible to track down.

Proposal: allow creating both a constified enum and a Rust enum, and autogenerate three conversion methods between them:

Input C/C++ Header

enum foo { one = 1, two = 2, three = 3 };

Bindgen Invocation

bindgen test.h bindgen test.h --rustified-enum '.*'

Actual Results

pub const foo_one: foo = 1; pub const foo_two: foo = 2; pub const foo_three: foo = 3; pub type foo = ::std::os::raw::c_uint;

#[repr(u32)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum foo { one = 1, two = 2, three = 3, }

Expected Results

Something like this:

pub const foo_one: foo = 1; pub const foo_two: foo = 2; pub const foo_three: foo = 3; pub type foo_ctype = ::std::os::raw::c_uint;

#[repr(u32)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum foo { one = 1, two = 2, three = 3, }

impl From for foo { fn from(value: foo_ctype) -> foo { match value{ 1 => foo::one, 2 => foo::two, 3 => foo::three, _ => panic!("unrecognized option for foo {foo_ctype}"), } } }

struct FooError(foo_ctype);

impl TryFrom for foo { type Error = FooError; fn try_from(value: foo_ctype) -> Result<foo, FooError> { match value{ 1 => Ok(foo::one), 2 => Ok(foo::two), 3 => Ok(foo::three), _ => Err(FooError(value)), } } }

impl foo { const unsafe fn from_ctype_unchecked(value: foo_ctype) -> Self { std::mem::transmute(value) } }

All bindings would use foo_ctype as the value type, but this would give an easy way to turn it into something exhaustive