hybrid_array/
from_fn.rs

1//! Support for constructing arrays using a provided generator function.
2
3use crate::{Array, ArraySize};
4use core::{
5    convert::Infallible,
6    mem::{self, MaybeUninit},
7    ptr,
8};
9
10impl<T, U> Array<T, U>
11where
12    U: ArraySize,
13{
14    /// Create array where each array element `T` is returned by the `f` call.
15    #[inline]
16    pub fn from_fn(mut f: impl FnMut(usize) -> T) -> Self {
17        Self::try_from_fn::<Infallible>(|n| Ok(f(n))).expect("should never fail")
18    }
19
20    /// Create array fallibly where each array element `T` is returned by the `f` call, or return
21    /// an error if any are encountered.
22    ///
23    /// # Errors
24    ///
25    /// Propagates the `E` type returned from the provided `F` in the event of error.
26    pub fn try_from_fn<E>(f: impl FnMut(usize) -> Result<T, E>) -> Result<Self, E> {
27        let mut array = Array::<MaybeUninit<T>, U>::uninit();
28        try_from_fn_erased(array.0.as_mut(), f)?;
29
30        // SAFETY: if we got here, every element of the array was initialized
31        Ok(unsafe { array.assume_init() })
32    }
33}
34
35/// Fills a `MaybeUninit` slice using the given fallible generator function.
36///
37/// Using a slice avoids monomorphizing for each array size.
38#[inline]
39fn try_from_fn_erased<T, E, F>(buffer: &mut [MaybeUninit<T>], mut f: F) -> Result<(), E>
40where
41    F: FnMut(usize) -> Result<T, E>,
42{
43    let mut guard = Guard {
44        array_mut: buffer,
45        initialized: 0,
46    };
47
48    while guard.initialized < guard.array_mut.len() {
49        let item = f(guard.initialized)?;
50
51        // SAFETY: the loop's condition ensures we won't push too many items
52        unsafe { guard.push_unchecked(item) };
53    }
54
55    mem::forget(guard);
56    Ok(())
57}
58
59/// Drop guard which tracks the total number of initialized items, and handles dropping them in
60/// the event a panic occurs.
61///
62/// Use `mem::forget` when the array has been fully constructed.
63struct Guard<'a, T> {
64    /// Array being constructed.
65    array_mut: &'a mut [MaybeUninit<T>],
66
67    /// Number of items in the array which have been initialized.
68    initialized: usize,
69}
70
71impl<T> Guard<'_, T> {
72    /// Push an item onto the guard, writing to its `MaybeUninit` slot and incrementing the
73    /// counter of the number of initialized items.
74    ///
75    /// # Safety
76    ///
77    /// This can only be called n-times for as many elements are in the slice.
78    #[inline]
79    pub unsafe fn push_unchecked(&mut self, item: T) {
80        // SAFETY: the `initialized` counter tracks the number of initialized items, so as long as
81        // this is called the correct number of times for the array size writes will always be
82        // in-bounds and to an uninitialized slot in the array.
83        unsafe {
84            self.array_mut
85                .get_unchecked_mut(self.initialized)
86                .write(item);
87            self.initialized = self.initialized.saturating_add(1);
88        }
89    }
90}
91
92impl<T> Drop for Guard<'_, T> {
93    fn drop(&mut self) {
94        debug_assert!(self.initialized <= self.array_mut.len());
95
96        // SAFETY: the loop only iterates over initialized items
97        unsafe {
98            let p: *mut T = self.array_mut.as_mut_ptr().cast();
99
100            for i in 0..self.initialized {
101                ptr::drop_in_place(p.add(i));
102            }
103        }
104    }
105}