49 lines
1.3 KiB
Rust
49 lines
1.3 KiB
Rust
use std::cell::RefCell;
|
|
use std::ops::Deref;
|
|
|
|
enum LazyInner<'a, T> {
|
|
Uncomputed(Option<Box<dyn FnOnce() -> T + 'a>>),
|
|
Computed(T),
|
|
}
|
|
|
|
pub struct Lazy<'a, T> {
|
|
inner: RefCell<LazyInner<'a, T>>,
|
|
}
|
|
impl<'a, T> Lazy<'a, T> {
|
|
pub fn new(func: Box<dyn FnOnce() -> T + 'a>) -> Lazy<T> {
|
|
Lazy {
|
|
inner: RefCell::new(LazyInner::Uncomputed(Some(func))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T: 'a> Deref for Lazy<'a, T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
unsafe fn extend_lifetime<'a, 'b, A>(a: &'a A) -> &'b A {
|
|
std::mem::transmute(a)
|
|
}
|
|
|
|
let mut inner_mut = self.inner.borrow_mut();
|
|
if let LazyInner::Uncomputed(func) = &mut *inner_mut {
|
|
if let Some(func) = func.take() {
|
|
*inner_mut = LazyInner::Computed(func());
|
|
} else {
|
|
panic!("Unreachable: uncomputed but no function to compute with")
|
|
}
|
|
}
|
|
|
|
match &*inner_mut {
|
|
LazyInner::Computed(computed) => unsafe {
|
|
// Extending the lifetime *should* be safe because we don't ever overwrite
|
|
// a computed value...
|
|
extend_lifetime(computed)
|
|
},
|
|
LazyInner::Uncomputed(_) => {
|
|
panic!("Unreachable: Should have been computed");
|
|
}
|
|
}
|
|
}
|
|
}
|