evobench_tools/utillib/
lazy.rs

1//! Easy lazily constructed values
2
3// (Didn't I have a lazy macro already?)
4
5// std::cell::LazyCell is an unstable feature; but also want to have
6// easy wrapper macros and usable `Result` handling, to achieve the
7// latter do not implement `Deref` (since that would have to panic to
8// unwrap the value from the Result). Given that methods are now
9// unambiguous, make `force` and the other accessors normal methods.
10
11// Want to avoid `force` to require &mut, hence need a cell type.
12// Because something needs to be stored from the start, OnceCell
13// doesn't work. RefCell would work but stores an additional flag. Go
14// unsafe.
15
16use std::{cell::UnsafeCell, fmt};
17
18/// TODO: update to use UnsafeCell like LazyResult
19pub enum Lazy<T, F: FnOnce() -> T> {
20    Thunk(F),
21    Poisoned,
22    Value(T),
23}
24
25impl<T, F: FnOnce() -> T> Lazy<T, F> {
26    pub fn new(f: F) -> Self {
27        Self::Thunk(f)
28    }
29
30    pub fn into_inner(self) -> Result<T, F> {
31        match self {
32            Lazy::Value(v) => Ok(v),
33            Lazy::Thunk(f) => Err(f),
34            Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
35        }
36    }
37
38    /// Forces evaluation if not already evaluated
39    pub fn into_value(self) -> T {
40        match self.into_inner() {
41            Ok(v) => v,
42            Err(f) => f(),
43        }
44    }
45
46    pub fn force(&mut self) -> &T {
47        match self {
48            Lazy::Thunk(_) => {
49                let mut thunk = Lazy::Poisoned;
50                std::mem::swap(self, &mut thunk);
51                match thunk {
52                    Lazy::Thunk(t) => {
53                        let mut new = Lazy::Value(t());
54                        std::mem::swap(self, &mut new);
55                        match self {
56                            Lazy::Value(v) => v,
57                            _ => panic!(),
58                        }
59                    }
60                    _ => panic!(),
61                }
62            }
63            Lazy::Value(v) => v,
64            Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
65        }
66    }
67
68    pub fn force_mut(&mut self) -> &mut T {
69        match self {
70            Lazy::Thunk(_) => {
71                let mut thunk = Lazy::Poisoned;
72                std::mem::swap(self, &mut thunk);
73                match thunk {
74                    Lazy::Thunk(t) => {
75                        let mut new = Lazy::Value(t());
76                        std::mem::swap(self, &mut new);
77                        match self {
78                            Lazy::Value(v) => v,
79                            _ => panic!(),
80                        }
81                    }
82                    _ => panic!(),
83                }
84            }
85            Lazy::Value(v) => v,
86            Lazy::Poisoned => panic!("Lazy instance has previously been poisoned"),
87        }
88    }
89}
90
91/// Variant that does not store error resuls from the thunk in the
92/// promise, so that Result or E do not need to support clone yet
93/// errors can still be propagated. It is at odds with FnOnce,
94/// though.
95enum InnerLazyResult<T, E, F: FnOnce() -> Result<T, E>> {
96    Thunk(F),
97    Poisoned,
98    Value(T),
99}
100
101pub struct LazyResult<T, E, F: FnOnce() -> Result<T, E>>(UnsafeCell<InnerLazyResult<T, E, F>>);
102
103impl<T, E, F: FnOnce() -> Result<T, E>> LazyResult<T, E, F> {
104    pub fn new(f: F) -> Self {
105        Self(UnsafeCell::new(InnerLazyResult::Thunk(f)))
106    }
107
108    pub fn into_inner(self) -> Result<T, F> {
109        match self.0.into_inner() {
110            InnerLazyResult::Value(v) => Ok(v),
111            InnerLazyResult::Thunk(f) => Err(f),
112            InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
113        }
114    }
115
116    /// Forces evaluation if not already evaluated
117    pub fn into_value(self) -> Result<T, E> {
118        match self.into_inner() {
119            Ok(v) => Ok(v),
120            Err(f) => f(),
121        }
122    }
123
124    pub fn force(&self) -> Result<&T, E> {
125        let rf = unsafe {
126            // Safe because access is private, Self is not Sync or
127            // Send, it is initialized, and swapped. Drop still runs
128            // on the UnsafeCell contents, as verified by the test
129            // further down.
130            &mut *self.0.get()
131        };
132        match rf {
133            InnerLazyResult::Thunk(_) => {
134                let mut thunk = InnerLazyResult::Poisoned;
135                std::mem::swap(rf, &mut thunk);
136                match thunk {
137                    InnerLazyResult::Thunk(t) => {
138                        let mut new = InnerLazyResult::Value(t()?);
139                        std::mem::swap(rf, &mut new);
140                        match rf {
141                            InnerLazyResult::Value(v) => Ok(v),
142                            _ => panic!(),
143                        }
144                    }
145                    _ => panic!(),
146                }
147            }
148            InnerLazyResult::Value(v) => Ok(v),
149            InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
150        }
151    }
152
153    pub fn force_mut(&mut self) -> Result<&mut T, E> {
154        let rf = self.0.get_mut();
155        match rf {
156            InnerLazyResult::Thunk(_) => {
157                let mut thunk = InnerLazyResult::Poisoned;
158                std::mem::swap(rf, &mut thunk);
159                match thunk {
160                    InnerLazyResult::Thunk(t) => {
161                        let mut new = InnerLazyResult::Value(t()?);
162                        std::mem::swap(rf, &mut new);
163                        match rf {
164                            InnerLazyResult::Value(v) => Ok(v),
165                            _ => panic!(),
166                        }
167                    }
168                    _ => panic!(),
169                }
170            }
171            InnerLazyResult::Value(v) => Ok(v),
172            InnerLazyResult::Poisoned => panic!("Lazy instance has previously been poisoned"),
173        }
174    }
175}
176
177impl<T: fmt::Debug, F: FnOnce() -> T> fmt::Debug for Lazy<T, F> {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        let mut d = f.debug_tuple("Lazy");
180        match self {
181            Self::Thunk(_) => d.field(&format_args!("<unforced>")),
182            Self::Poisoned => d.field(&format_args!("<poisoned>")),
183            Self::Value(v) => d.field(v),
184        };
185        d.finish()
186    }
187}
188
189impl<T: fmt::Debug, E, F: FnOnce() -> Result<T, E>> fmt::Debug for LazyResult<T, E, F> {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        let mut d = f.debug_tuple("LazyResult");
192        let rf = unsafe {
193            // See notes above, also, self is not called recursively
194            &*self.0.get()
195        };
196        match rf {
197            InnerLazyResult::Thunk(_) => d.field(&format_args!("<unforced>")),
198            InnerLazyResult::Poisoned => d.field(&format_args!("<poisoned>")),
199            InnerLazyResult::Value(v) => d.field(v),
200        };
201        d.finish()
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use std::cell::Cell;
208
209    use anyhow::Result;
210
211    use crate::lazyresult;
212
213    struct Foo<'t>(&'t Cell<i32>);
214
215    impl<'t> Drop for Foo<'t> {
216        fn drop(&mut self) {
217            self.0.set(self.0.get() + 1);
218        }
219    }
220
221    #[test]
222    fn t_() -> Result<()> {
223        let counter = Cell::new(0);
224        let l = lazyresult! {
225            anyhow::Ok(Foo(&counter))
226        };
227        // This must not compile:
228        // {
229        //     let (write, read) = std::sync::mpsc::channel();
230        //     let other_thread = std::thread::spawn(|| {
231        //         for msg in read {
232        //             dbg!("uh");
233        //         }
234        //     });
235        //     write.send(l);
236        // }
237        assert_eq!(counter.get(), 0);
238        let foo = l.force()?;
239        assert_eq!(foo.0.get(), 0);
240        drop(l);
241        assert_eq!(counter.get(), 1);
242
243        Ok(())
244    }
245}
246
247// I'd prefer the move keyword to be in front of the { } but that
248// doesn't work, lazy(move { }) is the next best (the { } are optional
249// though, but then emacs indentation won't work right if using more
250// than 1 expr).
251
252/// Construct a value lazily (for expressions that return `Result`,
253/// use `lazyresult`) (Todo: update to not require &mut)
254///
255/// Construct a `Lazy` value that runs the code body given to the
256/// macro once on the first call to `force`. Moves the captured values
257/// if a `move` keyword is given as the first item in the body. If the
258/// body evaluates to a `Result`, better use `lazyresult!` instead
259/// because `Result` is generally not cloneable and thus `force()?`
260/// won't work.
261#[macro_export]
262macro_rules! lazy {
263    { move $($body:tt)* } => {
264        $crate::lazy::Lazy::new(move || { $($body)* })
265    };
266    { $($body:tt)* } => {
267        $crate::lazy::Lazy::new(|| { $($body)* })
268    }
269}
270
271// XX: Should this be renamed to `try_lazy`, probably?
272/// Construct a value lazily, with the possibility for errors
273///
274/// Same as `lazy!` but `force` evaluates to a new `Result` each time,
275/// allowing `force()?` to work seamlessly; but since, currently,
276/// `force` consumes the captured closure when called the first time,
277/// your code has to avoid calling `force` again if it got an error
278/// (this is relying on the borrow checker being smart enough). Only
279/// the first `force` call can fail, subsequent ones always return Ok.
280#[macro_export]
281macro_rules! lazyresult {
282    { move $($body:tt)* } => {
283        $crate::utillib::lazy::LazyResult::new(move || { $($body)* })
284    };
285    { $($body:tt)* } => {
286        $crate::utillib::lazy::LazyResult::new(|| { $($body)* })
287    }
288}