use crate::{Array, ArraySize};
use core::{
convert::Infallible,
mem::{self, MaybeUninit},
ptr,
};
impl<T, U> Array<T, U>
where
U: ArraySize,
{
#[inline]
pub fn from_fn(mut f: impl FnMut(usize) -> T) -> Self {
Self::try_from_fn::<Infallible>(|n| Ok(f(n))).expect("should never fail")
}
pub fn try_from_fn<E>(f: impl FnMut(usize) -> Result<T, E>) -> Result<Self, E> {
let mut array = Array::<MaybeUninit<T>, U>::uninit();
try_from_fn_erased(array.0.as_mut(), f)?;
Ok(unsafe { array.assume_init() })
}
}
#[inline]
fn try_from_fn_erased<T, E, F>(buffer: &mut [MaybeUninit<T>], mut f: F) -> Result<(), E>
where
F: FnMut(usize) -> Result<T, E>,
{
let mut guard = Guard {
array_mut: buffer,
initialized: 0,
};
while guard.initialized < guard.array_mut.len() {
let item = f(guard.initialized)?;
unsafe { guard.push_unchecked(item) };
}
mem::forget(guard);
Ok(())
}
struct Guard<'a, T> {
array_mut: &'a mut [MaybeUninit<T>],
initialized: usize,
}
impl<T> Guard<'_, T> {
#[inline]
pub unsafe fn push_unchecked(&mut self, item: T) {
unsafe {
self.array_mut
.get_unchecked_mut(self.initialized)
.write(item);
self.initialized = self.initialized.saturating_add(1);
}
}
}
impl<T> Drop for Guard<'_, T> {
fn drop(&mut self) {
debug_assert!(self.initialized <= self.array_mut.len());
unsafe {
let p: *mut T = self.array_mut.as_mut_ptr().cast();
for i in 0..self.initialized {
ptr::drop_in_place(p.add(i));
}
}
}
}