wasmtime_internal_core/
array.rs1use core::mem::MaybeUninit;
4
5pub fn array_try_from_fn<E, T, const N: usize>(
34 mut cb: impl FnMut(usize) -> Result<T, E>,
35) -> Result<[T; N], E> {
36 let mut result: MaybeUninit<[T; N]> = MaybeUninit::uninit();
37 {
38 struct DropGuard<'a, T> {
39 slice: &'a mut [MaybeUninit<T>],
40 initialized: usize,
41 }
42 impl<T> Drop for DropGuard<'_, T> {
43 fn drop(&mut self) {
44 for slot in self.slice[..self.initialized].iter_mut() {
45 unsafe {
48 slot.assume_init_drop();
49 }
50 }
51 }
52 }
53 let mut guard = DropGuard {
54 slice: result.as_mut(),
55 initialized: 0,
56 };
57 for (i, slot) in guard.slice.iter_mut().enumerate() {
58 slot.write(cb(i)?);
59 guard.initialized = i + 1;
60 }
61 guard.initialized = 0;
63 }
64 unsafe { Ok(result.assume_init()) }
66}
67
68#[cfg(test)]
69mod test {
70 use super::array_try_from_fn;
71 use core::cell::Cell;
72 use std_alloc::rc::Rc;
73 use std_alloc::string::{String, ToString};
74
75 #[test]
77 fn array_try_from_fn_test() {
78 let array: Result<[u8; 5], _> = array_try_from_fn(|i| i.try_into());
79 assert_eq!(array, Ok([0, 1, 2, 3, 4]));
80
81 let array: Result<[i8; 200], _> = array_try_from_fn(|i| i.try_into());
82 assert!(array.is_err());
83 }
84
85 #[test]
86 fn smoke_try_from_fn() {
87 let arr = array_try_from_fn(|i| Ok::<_, ()>(i * 2)).unwrap();
88 assert_eq!(arr, [0, 2, 4, 6, 8]);
89 assert_eq!(
90 array_try_from_fn::<_, _, 3>(|i| if i == 0 { Ok(0) } else { Err(1) }).unwrap_err(),
91 1
92 )
93 }
94
95 #[test]
96 fn try_from_fn_dont_drop_on_success() {
97 let arr = array_try_from_fn(|i| Ok::<_, String>(i.to_string())).unwrap();
98 assert_eq!(arr, ["0", "1"]);
99 }
100
101 #[test]
102 fn try_from_fn_drop_on_failure() {
103 let drops = Rc::new(Cell::new(0));
104
105 struct DropCounter(Rc<Cell<usize>>);
106 impl Drop for DropCounter {
107 fn drop(&mut self) {
108 self.0.set(self.0.get() + 1);
109 }
110 }
111
112 let err = array_try_from_fn::<_, _, 10>(|i| match i {
113 0..=4 => Ok(DropCounter(drops.clone())),
114 _ => Err("error".to_string()),
115 })
116 .err()
117 .unwrap();
118 assert_eq!(err, "error");
119 assert_eq!(drops.get(), 5);
120 }
121
122 #[test]
123 #[cfg(feature = "std")]
124 fn try_from_fn_drop_on_panic() {
125 let drops = Rc::new(Cell::new(0));
126
127 struct DropCounter(Rc<Cell<usize>>);
128 impl Drop for DropCounter {
129 fn drop(&mut self) {
130 self.0.set(self.0.get() + 1);
131 }
132 }
133
134 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
135 array_try_from_fn::<_, _, 10>(|i| match i {
136 0..=4 => Ok::<_, String>(DropCounter(drops.clone())),
137 _ => panic!("hi"),
138 })
139 }));
140 assert!(result.is_err());
141 assert_eq!(drops.get(), 5);
142 }
143}