I am thinking of a pattern like the following. I have a State
that can generate all state changes legal in that state. Think: a board game and generating all legal moves in a given position. The StateChange
holds a reference to State
, thus maintaining the invariant that it is legal in that state. Then I pick one change, drop all the others, and want to apply it to the state.
This of course doesn't compile. I can't have a mutable reference and a shared reference at the same time.
However, logically this could be safe: this is the last remaining shared reference. Its lifetime could end in the middle of the apply
method, before any modifications to State are made by the method.
The problem is that the lifetime can't look "inside" the method and end in the middle of it.
I can think of two solutions:
-
Use
RefCell
insideState
.apply
would drop the last remaining shared reference before it acquires a mutable reference. But this adds a runtime cost that seems unnecessary -- the compiler "knows" that it is the last remaining shared reference based on static analysis. -
Have
apply
return a clone ofState
, rather than modifying in place. This adds an unnecessary cost of cloning, just before dropping the previous copy ofState
.
Any other ideas?
struct State;
struct StateChange<'a> {
state: &'a State
}
impl State {
fn possible_changes(&self) -> Vec<StateChange> {
todo!()
}
fn apply(&mut self, change: StateChange) {
todo!()
}
}
fn main() {
let mut state = State;
let mut changes = state.possible_changes();
let change = changes.pop().unwrap();
drop(changes);
state.apply(change);
}