I am adapting some state machine code I found in this blog post using Box<dyn State<S>>
to erase the state type. My problem: something in my trait definitions causes the borrow of the actually mutated state to be too long, but I am not seeing it.
Note: I know that strictly speaking I don't need any lifetimes here, but I want to be able to have state that contains borrows, e.g. struct Game<'a> { text: &'a str }
.
error[E0499]: cannot borrow `game` as mutable more than once at a time
--> src/main.rs:60:34
|
60 | state = match state.next(&mut game) {
| ^^^^^^^^^ `game` was mutably borrowed here in the previous iteration of the loop
Code: Rust Playground
struct Ping;
struct Pong;
#[derive(Debug)]
struct Game {
how_many_ping: usize,
how_many_pong: usize,
}
trait State<S> {
fn next(self: Box<Self>, state: &mut S) -> Transition<S>;
}
impl State<Game> for Ping {
fn next(self: Box<Self>, state: &mut Game) -> Transition<Game> {
state.how_many_ping += 1;
if state.how_many_ping > 100 {
return Transition::Complete(());
}
Transition::next(self, Pong)
}
}
impl State<Game> for Pong {
fn next(self: Box<Self>, state: &mut Game) -> Transition<Game> {
state.how_many_pong += 1;
if state.how_many_pong > 100 {
return Transition::Complete(());
}
Transition::next(self, Ping)
}
}
enum Transition<'a, S> {
Next(Box<dyn State<S> + 'a>),
Complete(()),
}
impl<'a, S> Transition<'a, S> {
fn next<'b, In, Out>(_: Box<In>, out: Out) -> Transition<'b, S>
where
In: State<S> + TransitionTo<Out>,
Out: State<S> +'b,
{
Transition::Next(Box::new(out))
}
}
trait TransitionTo<U> {}
impl TransitionTo<Ping> for Pong {}
impl TransitionTo<Pong> for Ping {}
fn main() {
let mut game = Game { how_many_ping: 0, how_many_pong: 0 };
let mut state: Box<dyn State<Game>> = Box::new(Ping);
loop {
state = match state.next(&mut game) {
Transition::Next(state) => state,
Transition::Complete(_) => break,
}
}
println!("{:#?}", game);
}