I'm trying to implement safe c-like enum's for Rust, because I'm hurt by incompatibility between C enum's and Rust #[repr(C)]
enum's. I'm looking for feeback. Here is my PoC.
Playground: Rust Playground
// Author: Volodymyr M. Lisivka <vlisivka@gmail.com>
pub trait IsEnumValid {
fn is_value_valid(repr: i32) -> bool;
}
/// The wrapper for C-like enum, which has same size as C enum.
#[derive(Copy, Clone)]
pub union CEnumI32Union<E>
where
E: Copy + IsEnumValid,
{
enum_repr: E,
int_repr: i32,
}
impl<E: Copy + IsEnumValid> CEnumI32Union<E> {
pub fn new(e: E) -> Self {
Self { enum_repr: e }
}
pub fn from_i32(i: i32) -> Self {
Self { int_repr: i }
}
pub fn from_safe(e: SafeCEnum<E>) -> Self {
match e {
SafeCEnum::ValidEnum(e) => Self { enum_repr: e },
SafeCEnum::UnknownValue(i) => Self { int_repr: i },
}
}
pub fn to_safe(&self) -> SafeCEnum<E> {
SafeCEnum::from(*self)
}
pub fn to_int(&self) -> i32 {
unsafe { self.int_repr }
}
pub fn is_valid(&self) -> bool {
unsafe { E::is_value_valid(self.int_repr) }
}
pub fn ok(&self) -> Option<E> {
unsafe {
if E::is_value_valid(self.int_repr) {
Some(self.enum_repr)
} else {
None
}
}
}
pub fn unwrap(&self) -> E {
self.ok().unwrap()
}
pub fn unwrap_or(&self, e: E) -> E {
self.ok().unwrap_or(e)
}
}
impl<E: Copy + IsEnumValid> PartialEq for CEnumI32Union<E> {
fn eq(&self, other: &Self) -> bool {
unsafe { self.int_repr == other.int_repr }
}
}
impl<E: Copy + IsEnumValid> Eq for CEnumI32Union<E> {}
impl<E: Copy + IsEnumValid> From<E> for CEnumI32Union<E> {
fn from(e: E) -> Self {
Self { enum_repr: e }
}
}
impl<E: Copy + IsEnumValid> From<i32> for CEnumI32Union<E> {
fn from(i: i32) -> Self {
CEnumI32Union { int_repr: i }
}
}
impl<E: Copy + IsEnumValid> From<CEnumI32Union<E>> for i32 {
fn from(e: CEnumI32Union<E>) -> Self {
unsafe { e.int_repr }
}
}
/// Safe represenation for C-like enum, which uses 2x size to C enum.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SafeCEnum<E>
where
E: Copy + IsEnumValid,
{
ValidEnum(E),
UnknownValue(i32),
}
impl<E: Copy + IsEnumValid> SafeCEnum<E> {
pub fn new(e: E) -> Self {
SafeCEnum::ValidEnum(e)
}
pub fn from_i32(i: i32) -> Self {
CEnumI32Union::from_i32(i).to_safe()
}
pub fn to_union(&self) -> CEnumI32Union<E> {
match *self {
SafeCEnum::ValidEnum(e) => CEnumI32Union::new(e),
SafeCEnum::UnknownValue(i) => CEnumI32Union::from_i32(i),
}
}
pub fn is_valid(&self) -> bool {
match self {
SafeCEnum::ValidEnum(_) => true,
SafeCEnum::UnknownValue(_) => false,
}
}
pub fn ok(&self) -> Option<E> {
match *self {
SafeCEnum::ValidEnum(e) => Some(e),
SafeCEnum::UnknownValue(_) => None,
}
}
pub fn unwrap(&self) -> E {
self.ok().unwrap()
}
pub fn unwrap_or(&self, e: E) -> E {
self.ok().unwrap_or(e)
}
}
impl<E: Copy + IsEnumValid> From<CEnumI32Union<E>> for SafeCEnum<E> {
fn from(e: CEnumI32Union<E>) -> Self {
unsafe {
if E::is_value_valid(e.int_repr) {
SafeCEnum::ValidEnum(e.enum_repr)
} else {
SafeCEnum::UnknownValue(e.int_repr)
}
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Enum1 {
Z = 0,
A = 1,
B = 2,
C = 4,
}
impl IsEnumValid for Enum1 {
fn is_value_valid(i: i32) -> bool {
i == Enum1::Z as i32 || i == Enum1::A as i32 || i == Enum1::B as i32 || i == Enum1::C as i32
}
}
fn main() {
println!(
"Size of CEnumI32Union<Enum1>: {}. Size of SafeCEnum<Enum1>: {}.",
std::mem::size_of::<CEnumI32Union<Enum1>>(),
std::mem::size_of::<SafeCEnum<Enum1>>(),
);
{
let en: CEnumI32Union<Enum1> = Enum1::A.into();
let int = en.to_int();
let safe_enum = en.to_safe();
let opt = en.ok();
println!(
"Safe enum: {:?}, int: {:?}, Option: {:?}, is_valid: {}, unwrap_or(Z): {:?}.",
safe_enum,
int,
opt,
en.is_valid(),
en.unwrap_or(Enum1::Z),
);
}
{
let en: CEnumI32Union<Enum1> = 123.into();
let int = en.to_int();
let safe_enum = en.to_safe();
let opt = en.ok();
println!(
"Safe enum: {:?}, int: {:?}, Option: {:?}, is_valid: {}, unwrap_or(Z): {:?}.",
safe_enum,
int,
opt,
en.is_valid(),
en.unwrap_or(Enum1::Z),
);
}
}