I'm trying to implement lazy "thunks" in Rust and I just can't figure out how to get my code to pass the borrow checker. The basic idea is that a Thunk<T>
can only be in one of two ThunkState
s:
Forced
which carries its value of typeT
;Unforced
, which carries a boxed closure that returns aT
.
My naïve code goes like this:
pub struct Thunk<T>(ThunkState<T>);
enum ThunkState<T> {
Forced(T),
Unforced(Box<Fn() -> T>),
}
impl<T> Thunk<T> {
pub fn new<F>(f: F) -> Thunk<T>
where
F: Fn() -> T + 'static,
{
Thunk(ThunkState::Unforced(Box::new(f)))
}
pub fn get(&mut self) -> &T {
match self.0 {
ThunkState::Forced(ref t) => &t,
ThunkState::Unforced(ref f) => {
// TROUBLE HERE
self.0 = ThunkState::Forced(f());
self.get()
}
}
}
}
I get the following two compilation errors:
error[E0506]: cannot assign to `self.0` because it is borrowed
--> src/main.rs:21:17
|
19 | ThunkState::Unforced(ref f) => {
| ----- borrow of `self.0` occurs here
20 | // TROUBLE HERE
21 | self.0 = ThunkState::Forced(f());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.0` occurs here
error[E0502]: cannot borrow `*self` as mutable because `self.0.0` is also borrowed as immutable
--> src/main.rs:22:17
|
19 | ThunkState::Unforced(ref f) => {
| ----- immutable borrow occurs here
...
22 | self.get()
| ^^^^ mutable borrow occurs here
23 | }
24 | }
| - immutable borrow ends here
I've gone through various iterations of trying out stuff (e.g., match *self.0
, using &mut
in the ThunkState
patterns, and a few variations), but try as I may, I can't figure out how to fix it.
- Am I attempting to do something that doesn't make sense?
- If not, what makes this example so tricky, and how do I get it right?
Staring at it a bit more, I've formulated the following hypothesis: the assignment to self.0
would invalidate the f
reference in that match branch. Is this right? And if so, then how do I achieve what I'm trying to do—discard the closure after I use it?