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