#enums #reference #ref #wrapper #proc-macro #data-access

no-std enum-ref

Proc. macro for generating enum discriminant types.

2 unstable releases

0.1.0 Mar 27, 2023
0.0.0 Mar 27, 2023

#1996 in Data structures

MIT license

9KB

[2]: https://github.com/Robbepop/enum-ref/actions/workflows/rust.yml [4]: https://docs.rs/enum-ref [6]: https://crates.io/crates/enum-ref

Continuous Integration Documentation Crates.io
[ci][2] [docs][4] [crates][6]

#[derive(EnumRef)] and #[derive(EnumMut)]

This crate provides the #[derive(EnumRef)] and #[derive(EnumMut)] proc. macros that generate reference wrappers for Rust enum types. Deriving EnumRef or EnumMut will also implement the respective trait which allows to access and instantiate the generated reference wrappers.

Motivation

The generated reference types are a bit different from default references to Rust enum instances in that only the enum data is a reference while the enum discriminant remains inline. When there is a need for having enum reference wrappers this crate is especially useful for when a user has enum types with a big number of variants that would make it labor intense to maintain a mirroring between original enum type and reference wrapper.

This might even yield a performance improvement if users mostly are interested in querying the enum discriminant. However, performance is not the primary use case of this crate.

My personal motivation for this crate is to allow for more space efficient enum encodings. Usually enum instances are encoded with an aligned discriminant and all variants share the same size_of with the biggest enum variant. This has the downside that the default Rust enum encoding potentially wastes a lot of space. When trying to encode Rust enum instances space-efficiently we still want to access the encoded enum instances, however we cannot use normal references to them since those references assume the aligned enum encoding which won't be the case. This is where our new reference wrapper types come into play since we can use them for our new encoding.

Usage

Below we demonstrate how to use the proc. macros provided by this crate.

#[derive(EnumRef, EnumMut)]
#[repr(u8)] // Rust requires this for `B = 42`
enum Test {
    A,
    B = 42,
    C(i32),
    D(i32, i64),
    E { a: i32 },
    F { a: i32, b: i64 },
}

// Access and name the generated `enum` reference wrapper types as follows:
type TestRef<'a> = <Test as EnumRef>::Ref<'a>;
type TestMut<'a> = <Test as EnumMut>::Mut<'a>;

// Creates reference wrappers of `enum` instances as follows:
let test = Test::C(42);
let test_ref: TestRef = <Test as EnumRef>::as_ref(&test);
match (&test, test_ref) {
    (Test::C(a0), TestRef::C(a1)) => assert_eq!(a0, a1),
    _ => panic!("something wen't wrong ..."),
}

Generated Code

The above #[derive(EnumRef)] for example will generate roughly the following Rust code:

const _: () = {
    #[derive(::core::fmt::Debug)]
    #[repr(u8)]
    pub enum TestRef<'__enum_ref_lt> {
        A,
        B = 42,
        C(&'__enum_ref_lt i32),
        D(&'__enum_ref_lt i32, &'__enum_ref_lt i64),
        E {
            a: &'__enum_ref_lt i32,
        },
        F {
            a: &'__enum_ref_lt i32,
            b: &'__enum_ref_lt i64,
        },
    }

    impl ::enum_ref::EnumRef for Test {
        type Ref<'__enum_ref_lt> where Self: '__enum_ref_lt =
                TestRef<'__enum_ref_lt> where Self: '__enum_ref_lt;
        fn as_ref(&self) -> <Self as ::enum_ref::EnumRef>::Ref<'_> {
            type __enum_ref_EnumRef_Ref<'__enum_ref_lt> =
                <Test as ::enum_ref::EnumRef>::Ref<'__enum_ref_lt>;
            match self {
                Self::A => __enum_ref_EnumRef_Ref::A,
                Self::B => __enum_ref_EnumRef_Ref::B,
                Self::C(_0) => __enum_ref_EnumRef_Ref::C(_0),
                Self::D(_0, _1) => __enum_ref_EnumRef_Ref::D(_0, _1),
                Self::E { a } => __enum_ref_EnumRef_Ref::E { a },
                Self::F { a, b } => __enum_ref_EnumRef_Ref::F { a, b },
            }
        }
    }
};

Dependencies

~235–680KB
~16K SLoC