boxed-array 0.1.0

Allows constructing boxed arrays directly onto the heap
Documentation
#![cfg_attr(not(feature = "std"), no_std)]

use alloc::{alloc::alloc, boxed::Box};
use core::{alloc::Layout, mem::size_of, ptr::NonNull};

extern crate alloc;

/// All useful implementations of this trait are already provided by this crate.
///
/// Indicates that the implementing type is an array that can be initialised with a closure.
///
/// The `F` generic type parameter is used to convince Rust to allow this -
/// it is normally (always?) inferred by the compiler.
pub unsafe trait BuildArray<C, F> {
    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut C));
}

unsafe impl<T, const N: usize> BuildArray<T, ()> for [T; N] {
    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut T)) {
        let space = space as *mut T;

        for n in 0..N {
            let container_ptr = space.add(n);
            build_component(container_ptr);
        }
    }
}

unsafe impl<T, Q, F, const N: usize> BuildArray<T, (F,)> for [Q; N]
where
    Q: BuildArray<T, F>,
{
    unsafe fn build_into(space: *mut Self, build_component: &mut impl FnMut(*mut T)) {
        let space = space as *mut Q;

        for n in 0..N {
            let container_ptr = space.add(n);
            Q::build_into(container_ptr, build_component);
        }
    }
}

/// Constructs an array directly on the heap.
///
/// The provided closure is called in memory-order to provide a value for
/// the next slot in the returned array.
///
/// The size and the dimensionality of the returned array is not limited.
pub fn with<T, A, F>(mut construct: impl FnMut() -> T) -> Box<A>
where
    A: BuildArray<T, F>,
{
    if size_of::<A>() == 0 {
        // Safe as A is a ZST.
        return unsafe { Box::from_raw(NonNull::dangling().as_mut()) };
    }

    // Safe as A is not a ZST
    let space = unsafe { alloc(Layout::new::<A>()) };

    #[cfg(all(test, not(miri)))]
    {
        for i in 0..size_of::<A>() {
            // Safe as space is currently just bytes
            unsafe { space.add(i).write(0x69) }
        }
    }

    // Safe as space is allocated according to A's layout
    let space = space as *mut A;

    // Safe as storage is valid and properly aligned
    let mut build_component = move |storage: *mut T| unsafe { storage.write(construct()) };

    unsafe {
        A::build_into(space, &mut build_component);
    }

    // Safe as space is allocated according to Box's rules and is properly initialised
    unsafe { Box::from_raw(space) }
}

/// Constructs an array of default values directly on the heap.
///
/// The size and the dimensionality of the returned array is not limited.
///
/// Due to the fact that [Default] is implemented for (some) arrays
/// where the contained type also provides [Default], the compiler may
/// struggle to infer all the types for this function when a multi-dimensional
/// array is requested. If this is the case, the function can be called
/// as `from_default::<T, _, _>()` where `T` is the type that you wish to
/// use the [Default] implementation of.
pub fn from_default<T, A, F>() -> Box<A>
where
    A: BuildArray<T, F>,
    T: Default,
{
    with(|| Default::default())
}

/// Constructs an array of cloned values directly on the heap.
///
/// The size and the dimensionality of the returned array is not limited.
pub fn from_cloned<T, A, F>(val: &T) -> Box<A>
where
    A: BuildArray<T, F>,
    T: Clone,
{
    with(|| val.clone())
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_it_works() {
        let mut n = 5;
        let thing: Box<[[[i32; 5]; 5]; 5]> = with(|| {
            n += 1;
            n
        });

        #[cfg(feature = "std")]
        println!("{:?}", thing);

        let thing2: Box<[[[i32; 5]; 5]; 5]> = from_default::<i32, _, _>();

        #[cfg(feature = "std")]
        println!("{:?}", thing2);
    }
}