inplace_box/
lib.rs

1#![allow(incomplete_features)]
2#![feature(
3    allocator_api,    // for Box::from_raw_in
4    coerce_unsized,
5    fn_traits,        // for forwarding `Fn` in `InplaceBox`
6    generic_const_exprs,
7    ptr_metadata,
8    tuple_trait,      // for generalization of closure arguments
9    unboxed_closures, // for forwarding `Fn` in `InplaceBox`
10    unsize,
11    specialization,   // for specialization of `ConvertIntoInplaceBox`;
12                      // `min_specialization` does not apply for user defined trait.
13)]
14#![warn(clippy::all)]
15#![cfg_attr(not(test), no_std)]
16extern crate alloc;
17
18mod impl_fn_traits;
19mod impl_new;
20
21use core::borrow::Borrow;
22use core::borrow::BorrowMut;
23use core::fmt;
24use core::fmt::Debug;
25use core::fmt::Display;
26use core::fmt::Pointer;
27use core::future::Future;
28use core::marker::PhantomData;
29use core::marker::Unsize;
30use core::mem;
31use core::mem::MaybeUninit;
32use core::ops::Deref;
33use core::ops::DerefMut;
34use core::panic::AssertUnwindSafe;
35use core::ptr;
36use core::ptr::Pointee;
37
38use mem::align_of;
39use mem::size_of;
40
41/// A container similar to `Box`, but without heap allocation.
42///
43/// It stores data inline within a fixed-size buffer.
44pub struct InplaceBox<T: ?Sized, const SIZE: usize> {
45    storage: MaybeUninit<[u8; SIZE]>,
46    vtable: AssertUnwindSafe<<T as Pointee>::Metadata>,
47    _phantom: PhantomData<T>,
48}
49
50impl<T: ?Sized, const SIZE: usize> InplaceBox<T, SIZE> {
51    /// Helper to verify that the `T` is indeed a `dyn Trait`.
52    const ASSERT_DYN_T: () = assert!(
53        core::mem::size_of::<&T>() == core::mem::size_of::<usize>() * 2,
54        "`InplaceBox` only works for `dyn Trait` types"
55    );
56
57    pub(crate) fn new_impl<'a, U: Sized + Unsize<T> + 'a>(value: U) -> Self {
58        struct AssertSize<ValueT: Sized, DestT: Sized>(
59            PhantomData<(ValueT, DestT)>,
60        );
61        impl<ValueT: Sized, DestT: Sized> AssertSize<ValueT, DestT> {
62            const ASSERT: () = assert!(
63                size_of::<ValueT>() <= size_of::<DestT>(),
64                "Insufficient size of `InplaceBox` to store the object"
65            );
66            const fn check() {
67                () = Self::ASSERT;
68            }
69        }
70        AssertSize::<U, MaybeUninit<[u8; SIZE]>>::check();
71        // SAFETY: Safe, since we just checked the size statically.
72        unsafe { Self::new_unchecked(value) }
73    }
74
75    /// Construct a new object in-place in this object, without checking the
76    /// size.
77    ///
78    /// The type of the value must be convertible to `dyn T`.
79    ///
80    /// This constructor is provided to allow constructing objects either in
81    /// [`InplaceBox`] of a certain size or on heap for larger sizes. Since
82    /// `if` conditions in the caller on the object size are not optimized
83    /// out in debug mode, the code wouldn't compile when statically checking
84    /// the size. With unchecked version, it's possible to have such
85    /// dynamically-switched generics.
86    ///
87    /// # Safety
88    ///
89    /// The caller needs to ensure that the size of the type `U` is less than or
90    /// equal to the `SIZE` of the `InplaceBox`. Only the type match and
91    /// alignment is checked statically by the compiler.
92    ///
93    /// Prefer to use the safe [`Self::new`] constructor which checks for the
94    /// size.
95    pub unsafe fn new_unchecked<'a, U: Sized + Unsize<T> + 'a>(
96        value: U,
97    ) -> Self {
98        struct AssertAlignment<ValueT: Sized, T: ?Sized>(
99            PhantomData<(ValueT, T)>,
100        );
101        impl<ValueT: Sized, T: ?Sized> AssertAlignment<ValueT, T> {
102            const ASSERT: () = assert!(
103                align_of::<ValueT>() <= align_of::<<T as Pointee>::Metadata>(),
104                "Value alignment exceeds maximum allowed alignment"
105            );
106            const fn check() {
107                () = Self::ASSERT;
108            }
109        }
110        AssertAlignment::<U, T>::check();
111        () = Self::ASSERT_DYN_T;
112
113        let value_ref: &T = &value;
114        let vtable = AssertUnwindSafe(ptr::metadata(value_ref));
115        let mut res = Self {
116            storage: MaybeUninit::uninit(),
117            vtable,
118            _phantom: PhantomData,
119        };
120        unsafe { res.storage.as_mut_ptr().cast::<U>().write(value) };
121        res
122    }
123
124    /// Get a pointer to the contained value
125    unsafe fn as_ptr(&self) -> *const T {
126        let data_ptr = self.storage.as_ptr() as *const ();
127        ptr::from_raw_parts(data_ptr, *self.vtable)
128    }
129
130    /// Get a mutable pointer to the contained value
131    unsafe fn as_mut_ptr(&mut self) -> *mut T {
132        let data_ptr = self.storage.as_mut_ptr() as *mut ();
133        ptr::from_raw_parts_mut(data_ptr, *self.vtable)
134    }
135}
136
137impl<T: ?Sized, const SIZE: usize> Deref for InplaceBox<T, SIZE> {
138    type Target = T;
139
140    fn deref(&self) -> &Self::Target {
141        unsafe { &*self.as_ptr() }
142    }
143}
144
145impl<T: ?Sized, const SIZE: usize> DerefMut for InplaceBox<T, SIZE> {
146    fn deref_mut(&mut self) -> &mut Self::Target {
147        unsafe { &mut *self.as_mut_ptr() }
148    }
149}
150
151impl<T: ?Sized, const SIZE: usize> AsRef<T> for InplaceBox<T, SIZE> {
152    fn as_ref(&self) -> &T {
153        self
154    }
155}
156
157impl<T: ?Sized, const SIZE: usize> AsMut<T> for InplaceBox<T, SIZE> {
158    fn as_mut(&mut self) -> &mut T {
159        self
160    }
161}
162
163impl<T: ?Sized, const SIZE: usize> Borrow<T> for InplaceBox<T, SIZE> {
164    fn borrow(&self) -> &T {
165        self
166    }
167}
168
169impl<T: ?Sized, const SIZE: usize> BorrowMut<T> for InplaceBox<T, SIZE> {
170    fn borrow_mut(&mut self) -> &mut T {
171        self
172    }
173}
174
175impl<T: ?Sized + Debug, const SIZE: usize> Debug for InplaceBox<T, SIZE> {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        Debug::fmt(&**self, f)
178    }
179}
180
181impl<T: ?Sized + Display, const SIZE: usize> Display for InplaceBox<T, SIZE> {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        Display::fmt(&**self, f)
184    }
185}
186
187impl<T: ?Sized, const SIZE: usize> Pointer for InplaceBox<T, SIZE> {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        let ptr: *const T = &**self;
190        Pointer::fmt(&ptr, f)
191    }
192}
193
194impl<T: ?Sized + Future, const SIZE: usize> Future for InplaceBox<T, SIZE> {
195    type Output = T::Output;
196
197    fn poll(
198        self: core::pin::Pin<&mut Self>,
199        cx: &mut core::task::Context<'_>,
200    ) -> core::task::Poll<Self::Output> {
201        // SAFETY: Safe, since we are just forwarding pinning to the inner
202        // member, which is also pinned by definition.
203        unsafe {
204            let s = self.get_unchecked_mut();
205            core::pin::Pin::new_unchecked(&mut **s).poll(cx)
206        }
207    }
208}
209
210impl<T: ?Sized, const SIZE: usize> Drop for InplaceBox<T, SIZE> {
211    fn drop(&mut self) {
212        unsafe {
213            ptr::drop_in_place(self.as_mut_ptr());
214        }
215    }
216}