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