aboutsummaryrefslogtreecommitdiff
path: root/src/lazy.rs
blob: b4f801b70d3aca3e3f15b77c5837d2871703f872 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#![macro_escape]

use std::ptr;
use std::cell::UnsafeCell;
use std::ops::Deref;
use std::boxed::FnBox;

pub enum State<V> {
    Evaluated(V),
    Evaluating,
    Unevaluated(Box<FnBox() -> V>)
}

pub struct Lazy<V> {
    state: UnsafeCell<State<V>>,
}

impl<V> Lazy<V> {
    pub fn new(f: Box<FnBox() -> V>) -> Lazy<V> {
        Lazy { state: UnsafeCell::new(State::Unevaluated(f)) }
    }

    pub fn ready(v: V) -> Lazy<V> {
        Lazy { state: UnsafeCell::new(State::Evaluated(v)) }
    }

    pub fn force(&mut self) {
        self.value();
    }

    pub fn unwrap(self) -> V {
        unsafe {
            match self.state.into_inner() {
                State::Unevaluated(f) => f(),
                State::Evaluating => panic!("Illegal state, can't call unwrap during evaluation"),
                State::Evaluated(v) => v
            }
        }
    }

    pub fn value(&self) -> &V {
        unsafe {
            let state = self.state.get();
            match *state {
                State::Evaluated(ref v) => v,
                State::Evaluating => panic!("Illegal state, can't call value during evaluation"),
                State::Unevaluated(_) => {
                    if let State::Unevaluated(f) = ptr::replace(state, State::Evaluating) {
                        ptr::replace(state, State::Evaluated(f()));
                    }
                    if let State::Evaluated(ref v) = *state { return v }
                    unreachable!()
                }
            }
        }
    }
}

impl<V> Deref for Lazy<V> {
    type Target = V;
    fn deref(&self) -> &V { self.value() }
}

#[macro_export]
macro_rules! lazy {
    (@as_expr $val: expr) => { $val };
    ($($val: tt)*) => { Lazy::new(Box::new(move || lazy![@as_expr { $($val)* }])) };
}

#[macro_export]
macro_rules! eager {
    (@as_expr $val: expr) => { $val };
    ($($val: tt)*) => { Lazy::<_>::ready(eager![@as_expr { $($val)* }]) };
}