enum_ptr/
lib.rs

1//! This crate provides a custom derive macro [`EnumPtr`] to generate bridges
2//! between an enum `T` and [`Compact<T>`] with minimum cost. [`Compact<T>`] is
3//! the compact representation of `T`, and it is only one pointer wide.
4//!
5//! This is viable because some types' low bits are always zeros.
6//! [`Compact<T>`] utilizes these bits to store the tag (discriminant).
7//!
8//! # Examples
9//!
10//! ```
11//! use enum_ptr::{Aligned, Compact, EnumPtr, ShiftUsize, Unit};
12//!
13//! # #[derive(Debug, PartialEq, Eq, Clone)]
14//! #[derive(EnumPtr)]
15//! #[repr(C, usize)] // required
16//! enum Foo<'a, T: Aligned> {
17//!     A(T),             // supports any `T: Aligned`
18//!     B(&'a u64),
19//!     C(Unit),          // use `Unit` for unit variants
20//!     D(ShiftUsize<3>), // you can even use non-pointers
21//! #    #[cfg(feature = "alloc")]
22//!     E(Box<i64>),
23//! }
24//!
25//! let compact_foo: Compact<_> = Foo::A(&1u64).into();
26//! let original_foo: Foo<_> = compact_foo.into();
27//! #
28//! # let test = |f: Foo<&u64>| assert_eq!(f.clone(), Foo::from(Compact::from(f)));
29//! # test(Foo::A(&0));
30//! # test(Foo::B(&1));
31//! # test(Foo::C(Unit::new()));
32//! # test(Foo::D(ShiftUsize::new(2)));
33//! # #[cfg(feature = "alloc")]
34//! # test(Foo::E(Box::new(3)));
35//! ```
36//!
37//! # Usage
38//!
39//! This crate provides multiple flavors of APIs.
40//!
41//! ## Flavor 1: copy everywhere
42//!
43//! If your enum type is [`Copy`] (e.g., consists of only `&T`s), you can
44//! mark it with `#[enum_ptr(copy)]`. Each time you need to use it, just copy
45//! and [`extract`](Compact::extract) it. Easy-peasy!
46//!
47//! Due to language limitations, we cannot automatically infer `copy`.
48//!
49//! <details>
50//! <summary>Click to show examples</summary>
51//!
52//! ```
53//! use enum_ptr::{Compact, EnumPtr};
54//!
55//! #[derive(EnumPtr, Debug, Clone, Copy, PartialEq, Eq)]
56//! #[enum_ptr(copy)] // required
57//! #[repr(C, usize)]
58//! enum Foo<'a, 'b> {
59//!     A(&'a i32),
60//!     B(&'b u32),
61//! }
62//!
63//! let foo: Compact<_> = Foo::A(&1).into();
64//! assert_eq!(foo.extract(), Foo::A(&1));
65//! assert_ne!(foo.extract(), Foo::B(&2));
66//! ```
67//! </details>
68//!
69//! ## Flavor 2: `get_ref` & `get_mut`
70//!
71//! If your enum type is not [`Copy`], and you happens to only have references
72//! to the compact value, you can use [`get_ref`] and [`get_mut`] to get
73//! references to **the object that it points to**.
74//!
75//! For example, if you hold a compact `Box<T>`, you can use these APIs to
76//! access `&T` and `&mut T`. Since there's no `Box<T>` in the memory (but only
77//! its compact form), we cannot create `&Box<T>` and `&mut Box<T>`. The target
78//! types are specified by [`FieldDeref`] and [`FieldDerefMut`].
79//!
80//! <details>
81//! <summary>Click to show examples</summary>
82//!
83//! ```
84//! # #[cfg(feature = "alloc")] {
85//! use enum_ptr::{get_mut, get_ref, Compact, EnumPtr};
86//!
87//! #[derive(EnumPtr)]
88//! #[repr(C, usize)]
89//! enum Foo {
90//!     A(Box<i32>),
91//!     B(Box<u32>),
92//! }
93//!
94//! let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
95//! assert_eq!(get_ref!(foo, Foo::A), Some(&1));
96//! assert_eq!(get_mut!(foo, Foo::A), Some(&mut 1));
97//! # }
98//! ```
99//! </details>
100//!
101//! ## Flavor 3: `borrow` & `borrow_mut`
102//!
103//! [`get_ref`] and [`get_mut`] can be troublesome if you want to deal with
104//! multiple variants at together. In that case, you can use
105//! [`borrow`](Compact::borrow) and [`borrow_mut`](Compact::borrow_mut). They
106//! will return derived reference types that you can `match`.
107//!
108//! Check the documentation of [`EnumPtr`] for more details.
109//!
110//! <details>
111//! <summary>Click to show examples</summary>
112//!
113//! ```
114//! # #[cfg(feature = "alloc")] {
115//! use enum_ptr::{Compact, EnumPtr};
116//!
117//! #[derive(EnumPtr, Debug)]
118//! #[enum_ptr(borrow, borrow_mut)] // required
119//! #[repr(C, usize)]
120//! enum Foo {
121//!     A(Box<i32>),
122//!     B(Option<Box<u32>>),
123//! }
124//!
125//! // enum FooRef<'enum_ptr> {
126//! //     A(&'enum_ptr i32),
127//! //     B(Option<&'enum_ptr u32>),
128//! // }
129//!
130//! // enum FooRefMut<'enum_ptr> {
131//! //     A(&'enum_ptr mut i32),
132//! //     B(Option<&'enum_ptr mut u32>),
133//! // }
134//!
135//! let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
136//! match foo.borrow() {
137//!     FooRef::A(inner) => assert_eq!(inner, &1),
138//!     _ => unreachable!(),
139//! }
140//! match foo.borrow_mut() {
141//!     FooRefMut::A(inner) => assert_eq!(inner, &mut 1),
142//!     _ => unreachable!(),
143//! }
144//! # }
145//! ```
146//! </details>
147//!
148//! ## Flavor 4: `map_ref` & `map_mut` *(legacy)*
149//!
150//! [`map_ref`](Compact::map_ref) and [`map_mut`](Compact::map_mut) will copy
151//! (= [`core::mem::transmute_copy`]) temporary objects out of its compact ones,
152//! that [`core::mem::forget`]-ed as soon as your closure ends, so that no
153//! destructor is needed to be run.  They are important for internal
154//! implementations, but less useful for lib users.
155//!
156//! <details>
157//! <summary>Click to show examples</summary>
158//!
159//! ```
160//! # #[cfg(feature = "alloc")] {
161//! use enum_ptr::{Compact, EnumPtr};
162//!
163//! #[derive(EnumPtr, Debug, PartialEq, Eq)]
164//! #[repr(C, usize)]
165//! enum Foo {
166//!     A(Box<i32>),
167//!     B(Box<u32>),
168//! }
169//!
170//! let mut foo: Compact<_> = Foo::A(Box::new(1)).into();
171//!
172//! let result = foo.map_ref(|f| match f {
173//!     Foo::A(r) => **r,
174//!     _ => unreachable!(),
175//! });
176//! assert_eq!(result, 1);
177//!
178//! unsafe {
179//!     foo.map_mut(|f| match f {
180//!         Foo::A(r) => **r = 2,
181//!         _ => unreachable!(),
182//!     });
183//! }
184//! assert_eq!(foo.extract(), Foo::A(Box::new(2)));
185//! # }
186//! ```
187//! </details>
188//!
189//! ## Extension
190//!
191//! Most of the important traits are public. You can implement them for your
192//! own types.
193//!
194//! - To make your types available in [`EnumPtr`], implement [`Aligned`].
195//! - To make your types available in [`get_ref`] / [`get_mut`] and
196//!   `#[enum_ptr(borrow)]` / `#[enum_ptr(borrow_mut)]`, implement
197//!   [`FieldDeref`] / [`FieldDerefMut`].
198//! - Unsatisfied with derived reference types? Implement [`CompactBorrow`] /
199//!   [`CompactBorrowMut`] by hand.
200//!
201//! # Limitations
202//!
203//! Suppose we are deriving from `Foo`, then
204//!
205//! - **`Foo` must have a `#[repr(C, usize)]`.**
206//!   - According to the [RFC] and the [Rust Reference], `#[repr(C, usize)]`
207//!     guarantees the memory layout and discriminant values. Thus, we can
208//!     safely transmute between two representations.
209//! - **Each variant of `Foo` must have exactly one field.**
210//!   - Unit variants are not allowed due to performance concerns.
211//!   - If you need a unit variant, use [`Unit`].
212//! - **Each variant of `Foo` must have enough alignment to store the tag.**
213//!   - Currently this crate cannot utilize high bits.
214//!
215//! Any violation of these rules will trigger a **compilation error**.
216//!
217//! [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md
218//! [Rust Reference]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
219//!
220//! # Features
221//!
222//! - `alloc` *(default)* --- `Box`, `Rc` and `Arc` support
223
224#![no_std]
225
226#[cfg(feature = "alloc")]
227extern crate alloc;
228
229mod base;
230mod traits;
231mod utils;
232
233pub use base::*;
234pub use traits::*;
235pub use utils::*;
236
237/// Derives conversions to and from [`Compact`].
238///
239/// # Examples
240///
241/// ```
242/// # #[cfg(feature = "alloc")] {
243/// use enum_ptr::{EnumPtr, Unit};
244///
245/// #[derive(EnumPtr)]
246/// #[enum_ptr(
247///     // copy,    // derives conversions to and from `CompactCopy`
248///     borrow(     // derives a reference type and `impl CompactBorrow`
249///         name = "FooRef",     // default: ident + "Ref"
250///         derive(Clone, Copy), // default: none
251///     ),
252///     borrow_mut( // derives a reference type and `impl CompactBorrowMut`
253///         name = "FooRefMut",  // default: ident + "RefMut"
254///         derive(Debug),       // default: none
255///     ),
256/// )]
257/// #[repr(C, usize)]
258/// enum Foo {
259///     // `borrow` / `borrow_mut` requires all unskipped fields
260///     // to implement `FieldDeref` / `FieldDerefMut`
261///     A(Box<i64>),         // ref type: `&i64` / `&mut i64`
262///     B(Option<Box<i64>>), // ref type: `Option<&i64>` / `Option<&mut i64>`
263///
264///     // use `skip` to skip both, or use `skip_borrow` / `skip_borrow_mut`
265///     #[enum_ptr(skip)]
266///     C(Unit),             // ref type: `PhantomData` (skipped)
267/// }
268/// # }
269/// ```
270pub use enum_ptr_derive::EnumPtr;