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:
- Safe panicking with
Into
- Safe but with an error with
TryInto
- Unsafe, assume you never get an unnamed value
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