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}