boxed_array/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3use alloc::{alloc::alloc, boxed::Box};
4use core::{alloc::Layout, mem::size_of, ptr::NonNull};
5
6extern crate alloc;
7
8/// All useful implementations of this trait are already provided by this crate.
9///
10/// Indicates that the implementing type is an array that can be initialised with a closure.
11///
12/// The `F` generic type parameter is used to convince Rust to allow this -
13/// it is normally (always?) inferred by the compiler.
14pub unsafe trait BuildArray<C, F> {
15    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut C));
16}
17
18unsafe impl<T, const N: usize> BuildArray<T, ()> for [T; N] {
19    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut T)) {
20        let space = space as *mut T;
21
22        for n in 0..N {
23            let container_ptr = space.add(n);
24            build_component(container_ptr);
25        }
26    }
27}
28
29unsafe impl<T, Q, F, const N: usize> BuildArray<T, (F,)> for [Q; N]
30where
31    Q: BuildArray<T, F>,
32{
33    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut T)) {
34        let space = space as *mut Q;
35
36        for n in 0..N {
37            let container_ptr = space.add(n);
38            Q::build_into(container_ptr, build_component);
39        }
40    }
41}
42
43/// Constructs an array directly on the heap.
44///
45/// The provided closure is called in memory-order to provide a value for
46/// the next slot in the returned array.
47///
48/// The size and the dimensionality of the returned array is not limited.
49pub fn with<T, A, F>(mut construct: impl FnMut() -> T) -> Box<A>
50where
51    A: BuildArray<T, F>,
52{
53    if size_of::<A>() == 0 {
54        // Safe as A is a ZST.
55        return unsafe { Box::from_raw(NonNull::dangling().as_mut()) };
56    }
57
58    // Safe as A is not a ZST
59    let space = unsafe { alloc(Layout::new::<A>()) };
60
61    #[cfg(all(test, not(miri)))]
62    {
63        for i in 0..size_of::<A>() {
64            // Safe as space is currently just bytes
65            unsafe { space.add(i).write(0x69) }
66        }
67    }
68
69    // Safe as space is allocated according to A's layout
70    let space = space as *mut A;
71
72    // Safe as storage is valid and properly aligned
73    let mut build_component = move |storage: *mut T| unsafe { storage.write(construct()) };
74
75    unsafe {
76        A::build_into(space, &mut build_component);
77    }
78
79    // Safe as space is allocated according to Box's rules and is properly initialised
80    unsafe { Box::from_raw(space) }
81}
82
83/// Constructs an array of default values directly on the heap.
84///
85/// The size and the dimensionality of the returned array is not limited.
86///
87/// Due to the fact that [Default] is implemented for (some) arrays
88/// where the contained type also provides [Default], the compiler may
89/// struggle to infer all the types for this function when a multi-dimensional
90/// array is requested. If this is the case, the function can be called
91/// as `from_default::<T, _, _>()` where `T` is the type that you wish to
92/// use the [Default] implementation of.
93pub fn from_default<T, A, F>() -> Box<A>
94where
95    A: BuildArray<T, F>,
96    T: Default,
97{
98    with(|| Default::default())
99}
100
101/// Constructs an array of cloned values directly on the heap.
102///
103/// The size and the dimensionality of the returned array is not limited.
104pub fn from_cloned<T, A, F>(val: &T) -> Box<A>
105where
106    A: BuildArray<T, F>,
107    T: Clone,
108{
109    with(|| val.clone())
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115    #[test]
116    fn test_it_works() {
117        let mut n = 5;
118        let thing: Box<[[[i32; 5]; 5]; 5]> = with(|| {
119            n += 1;
120            n
121        });
122
123        #[cfg(feature = "std")]
124        println!("{:?}", thing);
125
126        let thing2: Box<[[[i32; 5]; 5]; 5]> = from_default::<i32, _, _>();
127
128        #[cfg(feature = "std")]
129        println!("{:?}", thing2);
130    }
131}