use core::mem::{self, MaybeUninit};
extern crate alloc;
pub struct Guard<'a, T, const N: usize> {
array: &'a mut [MaybeUninit<T>; N],
initialized: usize,
}
impl<'a, T, const N: usize> Guard<'a, T, N> {
pub fn new(array: &'a mut [MaybeUninit<T>; N]) -> Self {
Self {
array,
initialized: 0,
}
}
pub unsafe fn push_unchecked(&mut self, value: T) {
debug_assert!(self.initialized < N);
unsafe { self.array.get_unchecked_mut(self.initialized).write(value) };
self.initialized += 1;
}
pub unsafe fn finalize(self) {
debug_assert_eq!(self.initialized, N);
mem::forget(self);
}
}
impl<T, const N: usize> Drop for Guard<'_, T, N> {
fn drop(&mut self) {
for i in 0..self.initialized {
unsafe { self.array.get_unchecked_mut(i).assume_init_drop() };
}
}
}
#[cfg(test)]
mod tests {
use super::Guard;
use core::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn init_tracking() {
let mut array = crate::uninit::<u32, 5>();
let mut guard = Guard::new(&mut array);
assert_eq!(0, guard.initialized);
unsafe { guard.push_unchecked(10) };
assert_eq!(1, guard.initialized);
}
#[test]
fn full_init() {
let mut array = crate::uninit::<u32, 3>();
let mut guard = Guard::new(&mut array);
unsafe {
guard.push_unchecked(1);
guard.push_unchecked(2);
guard.push_unchecked(3);
guard.finalize();
}
assert_eq!(*unsafe { crate::assume_init(array) }, [1, 2, 3]);
}
#[test]
fn partial_drop_cleanup() {
struct DropCounter {
counter: &'static AtomicUsize,
}
impl Drop for DropCounter {
fn drop(&mut self) {
self.counter.fetch_add(1, Ordering::SeqCst);
}
}
static DROPS: AtomicUsize = AtomicUsize::new(0);
{
let mut array = crate::uninit::<DropCounter, 5>();
let mut guard = Guard::new(&mut array);
unsafe {
guard.push_unchecked(DropCounter { counter: &DROPS });
guard.push_unchecked(DropCounter { counter: &DROPS });
}
}
assert_eq!(DROPS.load(Ordering::SeqCst), 2);
}
}