use std::cell::UnsafeCell;
pub struct OnceCell<T> {
inner: UnsafeCell<Option<T>>,
}
impl<T> OnceCell<T> {
pub fn new() -> OnceCell<T> { OnceCell { inner: UnsafeCell::new(None) } }
pub fn get(&self) -> Option<&T> {
unsafe { &*self.inner.get() }.as_ref()
}
pub fn set(&self, value: T) -> Result<(), T> {
let slot = unsafe { &mut *self.inner.get() };
if slot.is_some() {
return Err(value);
}
*slot = Some(value);
Ok(())
}
// UB!!!
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &T {
let slot = unsafe { &mut *self.inner.get() };
match slot {
None => {
*slot = Some(f());
slot.as_ref().unwrap()
}
Some(val) => val,
}
}
}
pub fn main() {
let cell: OnceCell<Box<i32>> = OnceCell::new();
let mut r1: Option<&i32> = None;
let r2: &i32 = cell.get_or_init(|| {
r1 = Some(cell.get_or_init(|| Box::new(1)));
// r1 = Some(&*cell.get_or_init(|| Box::new(1)));
Box::new(2)
});
let r1: &i32 = r1.unwrap();
println!("{} {}", r1, r2);
}
/*
result is like
~/rust ❯❯❯ ./once_cell
-1317687192 2
*/
Comments