tagged_box/
manually_impl_enum.rs

1//! A guide to manually implementing a tagged enum
2//!
3//! A good start is by looking at the code generated by [`tagged_box!`]. Given the following macro invocation:
4//!
5//! ```rust
6//! # use tagged_box::tagged_box;
7//! #
8//! tagged_box! {
9//!     struct Container, enum Item {
10//!         Integer(i32),
11//!         Boolean(bool),
12//!     }
13//! }
14//! ```
15//!
16//! This code will be generated:
17//!
18//! ```rust
19//! # use tagged_box::{TaggedBox, TaggableContainer, TaggableInner};
20//! # use core::mem::ManuallyDrop;
21//! #
22//! #[repr(transparent)]
23//! struct Container {
24//!     value: TaggedBox<Item>,
25//! }
26//!
27//! impl TaggableContainer for Container {
28//!     type Inner = Item;
29//!
30//!     fn into_inner(self) -> Self::Inner {
31//!         enum EnumCounter {
32//!             Integer,
33//!             Boolean,
34//!         }
35//!
36//!         unsafe {
37//!             match self.value.discriminant() {
38//!                 discrim if discrim == EnumCounter::Integer as _ =>
39//!                     Item::Integer(TaggedBox::into_inner(self.value)),
40//!                 discrim if discrim == EnumCounter::Boolean as _ =>
41//!                     Item::Boolean(TaggedBox::into_inner(self.value)),
42//!                 _ => panic!(),
43//!             }
44//!         }
45//!     }
46//! }
47//!
48//! enum Item {
49//!     Integer(i32),
50//!     Boolean(bool),
51//! }
52//!
53//! impl TaggableInner for Item {
54//!     fn into_tagged_box(self) -> TaggedBox<Self> {
55//!         enum EnumCounter {
56//!             Integer,
57//!             Boolean,
58//!         }
59//!
60//!         match self {
61//!             Self::Integer(value) => TaggedBox::new(value, EnumCounter::Integer as _),
62//!             Self::Boolean(value) => TaggedBox::new(value, EnumCounter::Boolean as _),
63//!         }
64//!     }
65//!
66//!     fn from_tagged_box(tagged: TaggedBox<Self>) -> Self {
67//!         enum EnumCounter {
68//!             Integer,
69//!             Boolean,
70//!         }
71//!
72//!         unsafe {
73//!             match tagged.discriminant() {
74//!                 discrim if discrim == EnumCounter::Integer as _ =>
75//!                     Self::Integer(TaggedBox::into_inner(tagged)),
76//!                 discrim if discrim == EnumCounter::Boolean as _ =>
77//!                     Self::Boolean(TaggedBox::into_inner(tagged)),
78//!                 _ => panic!(),
79//!             }
80//!         }
81//!     }
82//!
83//!     unsafe fn ref_from_tagged_box<F>(tagged: &TaggedBox<Self>, callback: F)
84//!     where
85//!         F: FnOnce(&Self),
86//!     {
87//!         enum EnumCounter {
88//!             Integer,
89//!             Boolean,
90//!         }
91//!
92//!         unsafe {
93//!             match tagged.discriminant() {
94//!                 discrim if discrim == EnumCounter::Integer as _ => {
95//!                     let variant = ManuallyDrop::new(Self::Integer(tagged.as_ptr::<i32>().read()));
96//!                     (callback)(&variant)
97//!                 }
98//!                 discrim if discrim == EnumCounter::Boolean as _ => {
99//!                     let variant = ManuallyDrop::new(Self::Boolean(tagged.as_ptr::<bool>().read()));
100//!                     (callback)(&variant)
101//!                 }
102//!                 _ => panic!(),
103//!             }
104//!         }
105//!     }
106//! }
107//! ```
108//!
109//! This is a lot of code, so we'll break it down a bit.  
110//! The first piece of code generated is this struct:
111//!
112//! ```rust
113//! # use tagged_box::{TaggedBox, TaggableContainer, TaggableInner};
114//! # enum Item {}
115//! #
116//! #[repr(transparent)]
117//! struct Container {
118//!     value: TaggedBox<Item>,
119//! }
120//! ```
121//!
122//! This is the 'handle' if you will. It allows you to generically hold multiple different instances
123//! of the same overarching enum that have different internal types. It's essentially an enum and a
124//! [`Box`] rolled into one. However, unlike leaving the safety guarantees up to you, the `tagged_box!`
125//! macro creates a default implementation that makes sure that everything goes smoothly.
126//!
127//! Next is the implementation of [`TaggableContainer`] for `Container`, which is the safe interface for
128//! retrieving the correct variant of the enum from the backing `TaggedBox`.
129//!
130//! Then we have the `Item` enum, which is a representation of what the `TaggedBox` is holding, and can be conveniently
131//! gotten from any `Container` instance. This has the [`TaggableInner`] trait implemented on it, which allows us to
132//! have convenient things like `Clone`, `PartialEq` and `Ord`.
133//!
134//! [`tagged_box!`]: ../macro.tagged_box.html
135//! [`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html
136//! [`TaggableContainer`]: crate::TaggableContainer
137//! [`TaggableInner`]: crate::TaggableInner