nospin 0.1.5

Rust crate containing an embedded version of a Mutex which can be used in `no_std` projects which are single-threaded.
Documentation
use core::{cell::UnsafeCell, convert::Infallible, mem::MaybeUninit};

pub struct Once<T = ()> {
    initialized: UnsafeCell<bool>,
    data: UnsafeCell<MaybeUninit<T>>,
}

unsafe impl<T: Send + Sync> Sync for Once<T> {}
unsafe impl<T: Send> Send for Once<T> {}

impl<T> Once<T> {
    #[allow(clippy::declare_interior_mutable_const)]
    pub const INIT: Self = Self {
        initialized: UnsafeCell::new(false),
        data: UnsafeCell::new(MaybeUninit::uninit()),
    };

    pub const fn new() -> Self {
        Self::INIT
    }

    fn is_initialized(&self) -> bool {
        unsafe { *self.initialized.get() }
    }

    unsafe fn force_get(&self) -> &T {
        unsafe { &*(*self.data.get()).as_ptr() }
    }

    unsafe fn force_get_mut(&mut self) -> &mut T {
        unsafe { &mut *(*self.data.get()).as_mut_ptr() }
    }

    pub fn call_once<F: FnOnce() -> T>(&self, f: F) -> &T {
        self.try_call_once(|| Ok::<T, Infallible>(f())).unwrap()
    }

    pub fn try_call_once<F: FnOnce() -> Result<T, E>, E>(&self, f: F) -> Result<&T, E> {
        unsafe {
            if self.is_initialized() {
                Ok(&*(*self.data.get()).as_ptr())
            } else {
                let value = f()?;
                (*self.data.get()).as_mut_ptr().write(value);
                *self.initialized.get() = true;
                Ok(&*(*self.data.get()).as_ptr())
            }
        }
    }

    pub fn get(&self) -> Option<&T> {
        unsafe { self.is_initialized().then(|| self.force_get()) }
    }

    pub fn get_mut(&mut self) -> Option<&mut T> {
        unsafe { self.is_initialized().then(|| self.force_get_mut()) }
    }
}

impl<T> Default for Once<T> {
    fn default() -> Self {
        Self::new()
    }
}