use std::ops::{Deref, DerefMut};
use std::cell::UnsafeCell;
use std::ptr;
use std::thunk::Invoke;
use self::Inner::{Evaluated, EvaluationInProgress, Unevaluated};
pub type Lazy<T> = Thunk<T>;
pub struct Thunk<T> {
inner: UnsafeCell<Inner<T>>,
}
impl<T> !Sync for Thunk<T> {}
impl<T> Thunk<T> {
pub fn new<F>(producer: F) -> Thunk<T>
where F: 'static + FnOnce() -> T {
Thunk {
inner: UnsafeCell::new(Unevaluated(Producer::new(producer))),
}
}
pub fn evaluated(val: T) -> Thunk<T> {
Thunk { inner: UnsafeCell::new(Evaluated(val)) }
}
pub fn force(&self) {
unsafe {
match *self.inner.get() {
Evaluated(_) => return,
EvaluationInProgress => {
panic!("Thunk::force called recursively. (A Thunk tried to force itself while trying to force itself).")
},
Unevaluated(_) => ()
}
match ptr::replace(self.inner.get(), EvaluationInProgress) {
Unevaluated(producer) => *self.inner.get() = Evaluated(producer.invoke()),
_ => debug_unreachable!()
};
}
}
pub fn unwrap(self) -> T {
self.force();
unsafe {
match self.inner.into_inner() {
Evaluated(val) => { val },
_ => debug_unreachable!()
}
}
}
}
struct Producer<T> {
inner: Box<Invoke<(), T> + 'static>
}
impl<T> Producer<T> {
fn new<F: 'static + FnOnce() -> T>(f: F) -> Producer<T> {
Producer {
inner: Box::new(move |()| {
f()
}) as Box<Invoke<(), T> + 'static>
}
}
fn invoke(self) -> T {
self.inner.invoke(())
}
}
enum Inner<T> {
Evaluated(T),
EvaluationInProgress,
Unevaluated(Producer<T>)
}
impl<T> Deref for Thunk<T> {
type Target = T;
fn deref<'a>(&'a self) -> &'a T {
self.force();
match unsafe { &*self.inner.get() } {
&Evaluated(ref val) => val,
_ => unsafe { debug_unreachable!() }
}
}
}
impl<T> DerefMut for Thunk<T> {
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
self.force();
match unsafe { &mut *self.inner.get() } {
&mut Evaluated(ref mut val) => val,
_ => unsafe { debug_unreachable!() }
}
}
}