Description
openedon Sep 23, 2023
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<foo_ctype> 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<foo_ctype> 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