[−][src]Module tagged_box::manually_impl_enum
A guide to manually implementing a tagged enum
A good start is by looking at the code generated by tagged_box!
. Given the following macro invocation:
tagged_box! { struct Container, enum Item { Integer(i32), Boolean(bool), } }
This code will be generated:
#[repr(transparent)] struct Container { value: TaggedBox<Item>, } impl TaggableContainer for Container { type Inner = Item; fn into_inner(self) -> Self::Inner { enum EnumCounter { Integer, Boolean, } unsafe { match self.value.discriminant() { discrim if discrim == EnumCounter::Integer as _ => Item::Integer(TaggedBox::into_inner(self.value)), discrim if discrim == EnumCounter::Boolean as _ => Item::Boolean(TaggedBox::into_inner(self.value)), _ => panic!(), } } } } enum Item { Integer(i32), Boolean(bool), } impl TaggableInner for Item { fn into_tagged_box(self) -> TaggedBox<Self> { enum EnumCounter { Integer, Boolean, } match self { Self::Integer(value) => TaggedBox::new(value, EnumCounter::Integer as _), Self::Boolean(value) => TaggedBox::new(value, EnumCounter::Boolean as _), } } fn from_tagged_box(tagged: TaggedBox<Self>) -> Self { enum EnumCounter { Integer, Boolean, } unsafe { match tagged.discriminant() { discrim if discrim == EnumCounter::Integer as _ => Self::Integer(TaggedBox::into_inner(tagged)), discrim if discrim == EnumCounter::Boolean as _ => Self::Boolean(TaggedBox::into_inner(tagged)), _ => panic!(), } } } unsafe fn ref_from_tagged_box<F>(tagged: &TaggedBox<Self>, callback: F) where F: FnOnce(&Self), { enum EnumCounter { Integer, Boolean, } unsafe { match tagged.discriminant() { discrim if discrim == EnumCounter::Integer as _ => { let variant = ManuallyDrop::new(Self::Integer(tagged.as_ptr::<i32>().read())); (callback)(&variant) } discrim if discrim == EnumCounter::Boolean as _ => { let variant = ManuallyDrop::new(Self::Boolean(tagged.as_ptr::<bool>().read())); (callback)(&variant) } _ => panic!(), } } } }
This is a lot of code, so we'll break it down a bit.
The first piece of code generated is this struct:
#[repr(transparent)] struct Container { value: TaggedBox<Item>, }
This is the 'handle' if you will. It allows you to generically hold multiple different instances
of the same overarching enum that have different internal types. It's essentially an enum and a
Box
rolled into one. However, unlike leaving the safety guarantees up to you, the tagged_box!
macro creates a default implementation that makes sure that everything goes smoothly.
Next is the implementation of TaggableContainer
for Container
, which is the safe interface for
retrieving the correct variant of the enum from the backing TaggedBox
.
Then we have the Item
enum, which is a representation of what the TaggedBox
is holding, and can be conveniently
gotten from any Container
instance. This has the TaggableInner
trait implemented on it, which allows us to
have convenient things like Clone
, PartialEq
and Ord
.