Fighting with borrow checker

Hey guys I'm new to rust and really want to learn it.
I often have problems with borrow checker in places that I fill I shouldn't

Here is my code. It's kind of a state machine where I can push, pop states in stack and replace the last one. I probably can fix the problem with borrow checker by poping the top state and push it back but I want to know how do you guys solve this problems because I fill there should be easy solution.

pub enum StateTransition {
    Push(Box<dyn State>),
    Transition(Box<dyn State>),
    Pop,
    Hold
}

pub trait State {
    fn update(&mut self,  data : &mut GameData, update_args : &UpdateArgs, event : Event) -> StateTransition;
    fn handleInput(&mut self, input : Input, time : Option<TimeStamp>, _data : &mut GameData);
    fn render(&mut self, c : Context,  g : &mut G2d, arguments : &RenderArgs, device : &mut gfx_device_gl::Device, resources : &mut GameResources, data : &GameData){}
    fn enter(&mut self, stateMachine : &mut StateMachine) {}
    fn exit(&mut self, stateMachine : &mut StateMachine) {}
}

pub struct StateMachine {
    stack : Vec<Box<dyn State>>
}

impl StateMachine {
    pub fn new(initial_state : Box<dyn State>) -> Result<StateMachine, Box<dyn error::Error>> {
        let stack = vec![initial_state];
        Ok(StateMachine{stack})
    }

    pub fn update(&mut self,  data : &mut GameData, update_args : &UpdateArgs, event : Event) -> bool {
        if let Some(top) = self.stack.last_mut() {
            let transition = top.update(data, update_args, event);
            match transition {
                StateTransition::Push(mut pushed_state) => {
                    pushed_state.enter( self);
                    self.stack.push(pushed_state);
                }

                StateTransition::Transition(transition) => {
                    top.exit( self);
                    *top = transition;
                    top.enter( self);

                }

                StateTransition::Pop => {
                    if let Some(mut top) = self.stack.pop() {
                        top.exit( self);
                    }
                }

                StateTransition::Hold => {}
            }
        }

        !self.stack.is_empty()
    }

    pub fn handleInput(&mut self, input : Input, time : Option<TimeStamp>, _data : &mut GameData) {

    }

    pub fn render(&mut self, c : Context,  g : &mut G2d, arguments : &RenderArgs, device : &mut gfx_device_gl::Device, resources : &mut GameResources, data : &GameData) {
        if let Some(top) = self.stack.last_mut() {
            top.render(c, g, arguments, device, resources, data);
        }
    }
}

pub fn renderText(text : &str, font : &mut Glyphs, transform: Matrix2d, draw_state: &DrawState, g : &mut G2d, selected : bool) {
    let color_pair : (f32,f32) = if selected {
        (0.2, 1.0)
    } else {
        (1.0, 0.2)
    };

    text::Text::new_color([color_pair.0, color_pair.1, 0.0, 1.0], 128).draw(
        text,
        font,
        draw_state,
        transform,
        g
    ).unwrap();
}

The error

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> tetris/src/state_machine.rs:61:31
   |
52 |         if let Some(top) = self.stack.last_mut() {
   |                            ---------- first mutable borrow occurs here
...
61 |                     top.exit( self);
   |                               ^^^^ second mutable borrow occurs here
62 |                     *top = transition;
   |                     ---- first borrow later used here

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> tetris/src/state_machine.rs:61:31
   |
52 |         if let Some(top) = self.stack.last_mut() {
   |                            ---------- first mutable borrow occurs here
...
61 |                     top.exit( self);
   |                               ^^^^ second mutable borrow occurs here
62 |                     *top = transition;
   |                     ---- first borrow later used here

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> tetris/src/state_machine.rs:63:32
   |
52 |         if let Some(top) = self.stack.last_mut() {
   |                            ---------- first mutable borrow occurs here
...
63 |                     top.enter( self);
   |                         -----  ^^^^ second mutable borrow occurs here
   |                         |
   |                         first borrow later used by call

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> tetris/src/state_machine.rs:63:32
   |
52 |         if let Some(top) = self.stack.last_mut() {
   |                            ---------- first mutable borrow occurs here
...
63 |                     top.enter( self);
   |                         -----  ^^^^ second mutable borrow occurs here
   |                         |
   |                         first borrow later used by call

Flattening the code can sometimes help;

        let transition = if let Some(top) = self.stack.last_mut() {
            top.update(data, update_args, event)
        } else {
            StateTransition::Hold
        };
        
        match transition {

Try using pop + push instead of last_mut, because last_mut creates a read-write lock on the vector and that creates borrow checker headaches in your case. Instead of *top = transition, you would then simply push(transition) and drop the top value, afterwards. In all other cases, you simply push(top) when you're done using it.

1 Like

Thanks. I did that as temporal solution, but it look unnatural to me

While it is good idea. It's not working

    pub fn update(&mut self,  data : &mut GameData, update_args : &UpdateArgs, event : Event) -> bool {

        let transition = if let Some(top) = self.stack.last_mut() {
            top.update(data, update_args, event)
        } else {
            StateTransition::Hold
        };

        match transition {
            StateTransition::Push(mut pushed_state) => {
                pushed_state.enter( self);
                self.stack.push(pushed_state);
            }

            StateTransition::Transition(mut transition) => {
                let top = self.stack.last_mut().unwrap();
                top.exit( self);
                *top = transition;
                top.enter( self);
            }

            StateTransition::Pop => {
                if let Some(mut top) = self.stack.pop() {
                    top.exit( self);
                }
            }

            StateTransition::Hold => {}
        }

        !self.stack.is_empty()
    }

This works but is kind of unnatural to me

    pub fn update(&mut self,  data : &mut GameData, update_args : &UpdateArgs, event : Event) -> bool {

        let transition = if let Some(top) = self.stack.last_mut() {
            top.update(data, update_args, event)
        } else {
            StateTransition::Hold
        };

        match transition {
            StateTransition::Push(mut pushed_state) => {
                pushed_state.enter( self);
                self.stack.push(pushed_state);
            }

            StateTransition::Transition(mut transition) => {
                let mut top = self.stack.pop().unwrap();
                top.exit( self);
                transition.enter(self);
                self.stack.push(transition);
            }

            StateTransition::Pop => {
                if let Some(mut top) = self.stack.pop() {
                    top.exit( self);
                }
            }

            StateTransition::Hold => {}
        }

        !self.stack.is_empty()
    }

I just want to know what is the "idiomatic" way to solve similar problems

Not an expert on stacks, state transitions, but poping and pushing stacks looks quite natural. It seems semantic, and is clear what's happening (in my mind).

What about it feels unnatural to you?

You can't use Rust references (temporary borrows) as if they were pointers. &mut is an exclusive lock on an object.

&mut self means that when this method is called nothing else can even exist that could directly read or write anything inside self.

As soon as you call self.stack.last_mut(), it locks self.stack for exclusive access by top and only top and absolutely nothing else for as long as top exists. That makes &mut self impossible to use, because self.stack is exclusively borrowed by top, so you can't give exclusive control of all of self any more.

You have to design your program around that. If you must use exclusive borrows, use them for as short as possible. That's why others have suggested push/pop, so that you don't keep the whole function locked around top.

It's not always necessary to use &mut, even for mutable data. Some interfaces use &self and interior mutability wrappers to mutate from behind a shared reference. You're going to need Arc<Mutex> or such for long-lived or shared mutable objects, because &mut is inappropriate for them and is going to cause you borrow checker nightmares.

Rust really doesn't like mutability and arbitrary references, so game designs where any object can reference and mutate any other object are going to be hard. Consider using ECS:

2 Likes

Thanks for your answer. Frankly I hate interior mutability and it's too early for me to try ecs. Probably I should rethink my old habits of programming if I want to stick with rust

Sounds like Roguelike Tutorial - In Rust might be just the thing for you. It's an in-depth tutorial that uses both Rust and ECS, but doesn't require expertise in either.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.