#![cfg_attr(not(test), no_std)]
#![cfg_attr(feature = "nightly", feature(allocator_api, maybe_uninit_uninit_array,))]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::convert::Infallible;
use core::mem::{forget, transmute_copy, MaybeUninit};
use core::ptr::{drop_in_place, slice_from_raw_parts_mut};
mod array_ext;
#[cfg_attr(not(feature = "nightly"), path = "stable.rs")]
#[cfg_attr(feature = "nightly", path = "nightly.rs")]
#[cfg(feature = "alloc")]
mod boxed;
#[cfg(feature = "alloc")]
pub use boxed::*;
pub use array_ext::*;
#[inline]
pub fn try_init_slice<T, E, F: FnMut(usize) -> Result<T, E>>(
s: &mut [MaybeUninit<T>],
mut f: F,
) -> Result<&mut [T], E> {
struct Guard<T> {
ptr: *mut T,
len: usize,
}
impl<T> Drop for Guard<T> {
fn drop(&mut self) {
unsafe { drop_in_place(slice_from_raw_parts_mut(self.ptr, self.len)) };
}
}
let mut guard = Guard { ptr: s.as_mut_ptr() as *mut T, len: 0 };
for (i, a) in s.iter_mut().enumerate() {
a.write(f(i)?);
guard.len += 1;
}
forget(guard);
Ok(unsafe { &mut *(s as *mut [MaybeUninit<T>] as *mut [T]) })
}
#[inline]
pub fn init_slice<T, F: FnMut(usize) -> T>(s: &mut [MaybeUninit<T>], mut f: F) -> &mut [T] {
unwrap_infallible(try_init_slice(s, |i| Ok(f(i))))
}
pub(crate) fn unwrap_infallible<T>(r: Result<T, Infallible>) -> T {
r.unwrap_or_else(|i| match i {})
}
#[inline]
pub fn init_array<T, F, const N: usize>(mut f: F) -> [T; N]
where
F: FnMut(usize) -> T,
{
unwrap_infallible(try_init_array(|i| Ok(f(i))))
}
#[inline]
pub fn try_init_array<T, E, F, const N: usize>(f: F) -> Result<[T; N], E>
where
F: FnMut(usize) -> Result<T, E>,
{
let mut arr = unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() };
try_init_slice(&mut arr, f)?;
Ok(unsafe { transmute_copy(&arr) })
}
#[cfg(test)]
mod tests {
use std::{
panic::catch_unwind,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
use super::*;
#[test]
fn array() {
assert_eq!(init_array::<_, _, 3>(|_| 0), [0, 0, 0]);
assert_eq!(init_array::<_, _, 3>(|i| i), [0, 1, 2]);
assert_eq!(init_array::<_, _, 5>(|i| i * i), [0, 1, 4, 9, 16]);
}
#[cfg(feature = "alloc")]
#[test]
fn boxed_array() {
assert_eq!(*init_boxed_array::<_, _, 3>(|_| 0), [0, 0, 0]);
assert_eq!(*init_boxed_array::<_, _, 3>(|i| i), [0, 1, 2]);
assert_eq!(*init_boxed_array::<_, _, 5>(|i| i * i), [0, 1, 4, 9, 16]);
}
#[cfg(feature = "alloc")]
#[test]
fn boxed_slice() {
assert_eq!(*init_boxed_slice(3, |_| 0), [0, 0, 0]);
assert_eq!(*init_boxed_slice(3, |i| i), [0, 1, 2]);
assert_eq!(*init_boxed_slice(5, |i| i * i), [0, 1, 4, 9, 16]);
}
#[cfg(feature = "alloc")]
#[test]
fn readme_example() {
let arr = init_array(|i| i * i);
assert_eq!(arr, [0, 1, 4, 9, 16]);
let arr = init_boxed_array(|i| i * i);
assert_eq!(arr, Box::new([0, 1, 4, 9, 16]));
let arr = init_boxed_slice(5, |i| i * i);
assert_eq!(&*arr, &[0, 1, 4, 9, 16]);
let mut state = 0;
let arr = init_array(move |i| {
state += i + 1;
state
});
assert_eq!(arr, [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]);
}
#[test]
fn drop() {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
struct Foo;
impl Foo {
fn new() -> Self {
COUNTER.fetch_add(1, SeqCst);
Self
}
}
impl Drop for Foo {
fn drop(&mut self) {
COUNTER.fetch_sub(1, SeqCst);
}
}
let _ = catch_unwind(|| {
init_array::<_, _, 10>(|i| {
if i == 7 {
assert_eq!(COUNTER.load(SeqCst), 6);
panic!()
}
Foo::new()
});
assert_eq!(COUNTER.load(SeqCst), 0);
});
#[cfg(feature = "alloc")]
let _ = catch_unwind(|| {
init_boxed_array::<_, _, 10>(|i| {
if i == 7 {
assert_eq!(COUNTER.load(SeqCst), 6);
panic!()
}
Foo::new()
});
assert_eq!(COUNTER.load(SeqCst), 0);
});
#[cfg(feature = "alloc")]
let _ = catch_unwind(|| {
init_boxed_slice(10, |i| {
if i == 7 {
assert_eq!(COUNTER.load(SeqCst), 6);
panic!()
}
Foo::new()
});
assert_eq!(COUNTER.load(SeqCst), 0);
});
}
}