#![doc = include_str!("../README.md")]
#![no_std]
use core::{fmt::{self, Debug}, mem::replace};
#[doc = include_str!("../README.md")]
pub struct LazyMut<T, F = fn() -> T> {
state: State<T, F>,
}
impl<T: Default> Default for LazyMut<T> {
fn default() -> Self {
Self::new(T::default)
}
}
impl<T: Debug, F> Debug for LazyMut<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut t = f.debug_tuple("LazyMut");
match self.state.try_get() {
Some(val) => t.field(val),
None => t.field(&format_args!("<uninit>")),
};
t.finish()
}
}
impl<T, F> LazyMut<T, F> {
pub const fn new(f: F) -> Self {
Self { state: State::Uninit(f) }
}
pub const fn try_get(&self) -> Option<&T> {
self.state.try_get()
}
pub const fn try_get_mut(&mut self) -> Option<&mut T> {
self.state.try_get_mut()
}
pub fn into_inner(self) -> Option<T> {
match self.state {
State::Uninit(_) => None,
State::Poisoned => panic_poisoned(),
State::Inited(val) => Some(val),
}
}
}
impl<T, F: FnOnce() -> T> LazyMut<T, F> {
pub fn get(&mut self) -> &mut T {
self.state.get_or_init()
}
}
#[derive(Clone)]
enum State<T, F> {
Uninit(F),
Inited(T),
Poisoned,
}
impl<T: Default, F> Default for State<T, F> {
fn default() -> Self {
Self::Inited(Default::default())
}
}
impl<T, F> State<T, F>
where F: FnOnce() -> T,
{
fn get_or_init(&mut self) -> &mut T {
match self {
State::Poisoned => panic_poisoned(),
State::Inited(val) => val,
State::Uninit(_) => {
let this = replace(self, Self::Poisoned);
let Self::Uninit(f) = this else { unreachable!() };
*self = State::Inited(f());
self.try_get_mut().unwrap()
},
}
}
}
impl<T, F> State<T, F> {
const fn try_get(&self) -> Option<&T> {
if let Self::Inited(v) = self {
Some(v)
} else {
None
}
}
const fn try_get_mut(&mut self) -> Option<&mut T> {
if let Self::Inited(v) = self {
Some(v)
} else {
None
}
}
}
#[cold]
#[inline(never)]
fn panic_poisoned() -> ! {
panic!("LazyMut instance has previously been poisoned")
}