default_boxed/
lib.rs

1#![cfg_attr(not(doctest), no_std)]
2
3//! Helper trait to create instances of large structs with default value on heap directly
4//! without going through stack.
5//!
6//! Similar to the unstable `box` syntax,
7//! it semantically doesn't require creating the whole struct on stack then moving to heap,
8//! and thus unlike [`copyless`][copyless] or [`boxext`][boxext],
9//! it doesn't rely on optimization to eliminate building the struct on stack,
10//! which may still face stack overflow on debug build when creating large struct.
11//!
12//! [copyless]: https://crates.io/crates/copyless
13//! [boxext]: https://crates.io/crates/boxext
14//!
15//! ## Example
16//!
17//! ```
18//! use default_boxed::DefaultBoxed;
19//!
20//! # #[cfg(not(miri))]
21//! const BASE: usize = 1024;
22//! # #[cfg(miri)]
23//! # const BASE: usize = 1;
24//!
25//! #[derive(DefaultBoxed)]
26//! struct Foo {
27//!     a: Bar,
28//!     b: [Bar; 1024 * BASE],
29//!     c: [u32; 1024 * BASE],
30//! }
31//!
32//! struct Bar(u16);
33//! impl Default for Bar {
34//!     fn default() -> Bar {
35//!         Bar(29)
36//!     }
37//! }
38//!
39//! let foo = Foo::default_boxed();
40//! assert_eq!(foo.a.0, 29);
41//! assert_eq!(foo.b[128 * BASE].0, 29);
42//! assert_eq!(foo.c[256 * BASE], 0);
43//!
44//! let foo_arr = Foo::default_boxed_array::<16>();
45//! assert_eq!(foo_arr[15].a.0, 29);
46//! ```
47
48extern crate alloc;
49
50use alloc::alloc::{alloc as alloc_raw, handle_alloc_error, Layout};
51use alloc::boxed::Box;
52use core::ptr;
53
54pub use default_boxed_derive::DefaultBoxed;
55
56/// Helper trait to create a boxed instance of the given type with a default value for each field.
57///
58/// This trait can be derived for structs.
59///
60/// To derive this trait, each field needs to also implement this trait, but all types which
61/// implements `Default` implements this trait via the blanket `impl` already.
62///
63/// In addition, if a field is an array, only the item type needs to implement this trait, and each
64/// item would be initialized separately.
65///
66/// # Safety
67///
68/// Implementations must ensure that `default_in_place` initializes the value on the given pointer.
69pub unsafe trait DefaultBoxed {
70    /// Create a boxed instance with default value for each field.
71    fn default_boxed() -> Box<Self>
72    where
73        Self: Sized,
74    {
75        let layout = Layout::new::<Self>();
76        unsafe {
77            if layout.size() == 0 {
78                return Box::from_raw(ptr::NonNull::<Self>::dangling().as_ptr());
79            }
80            let raw = alloc_raw(layout) as *mut Self;
81            if raw.is_null() {
82                handle_alloc_error(layout)
83            } else {
84                Self::default_in_place(raw);
85                Box::from_raw(raw)
86            }
87        }
88    }
89
90    /// Create a boxed array of the given size with default value of the type.
91    ///
92    /// ```
93    /// use default_boxed::DefaultBoxed;
94    /// let arr = u32::default_boxed_array::<32>();
95    /// assert_eq!(arr, Box::new([0; 32]));
96    /// ```
97    fn default_boxed_array<const N: usize>() -> Box<[Self; N]>
98    where
99        Self: Sized,
100    {
101        let layout = Layout::new::<[Self; N]>();
102        unsafe {
103            if layout.size() == 0 {
104                return Box::from_raw(ptr::NonNull::<[Self; N]>::dangling().as_ptr());
105            }
106            let raw = alloc_raw(layout) as *mut Self;
107            if raw.is_null() {
108                handle_alloc_error(layout)
109            } else {
110                for i in 0..N as isize {
111                    Self::default_in_place(raw.offset(i));
112                }
113                Box::from_raw(raw as *mut [Self; N])
114            }
115        }
116    }
117
118    /// Fill the given memory location with default value.
119    ///
120    /// # Safety
121    ///
122    /// For callers, behavior is undefined if `ptr` is not valid for writes, or it is not properly
123    /// aligned.
124    ///
125    /// For impls, behavior is undefined if this method reads from `ptr`.
126    unsafe fn default_in_place(ptr: *mut Self);
127}
128
129unsafe impl<T: Default> DefaultBoxed for T {
130    unsafe fn default_in_place(ptr: *mut Self) {
131        ptr::write(ptr, Default::default());
132    }
133}