rustborrow

multiple self borrows within trait default methods


I am running into issues with the borrow checker. I have a trait (Physics) which has getters (e.g. velocity) and setters (e.g. velocity_mut). It also has default methods accelerate and apply_force which uses the getters and setters. Why is it not good to have multiple borrows and what are the ways around this? Here's an error message:

       13:18    rustc           error       cannot borrow `*self` as immutable because it is also borrowed as mutable
                                            immutable borrow occurs here
       12:11    rustc           hint        mutable borrow occurs here
       12:11    rustc           hint        argument requires that `*self` is borrowed for `'static`
use gdnative::prelude::*;

// Running into issues with the borrow checker

trait Physics {
    fn apply_force(&mut self, force: &mut Vector2, truncate: bool) {
        let max_force = self.max_force();

        if force.square_length() > max_force * max_force && truncate {
            *force = force.normalize() * max_force;
        }
        let a = self.acceleration_mut();// Line 12
        *a += *force / self.mass();//Line 13
        self.accelerate(truncate);
    }
    fn accelerate(&mut self, truncate: bool) {
        let v = self.velocity_mut();
        *v += self.acceleration();

        let max_speed = self.max_speed();

        if v.square_length() > max_speed * max_speed && truncate {
            *v = v.normalize() * max_speed;
        }
    }
    fn velocity(&self) -> Vector2;
    fn velocity_mut(&mut self) -> &mut Vector2;
    fn acceleration(&self) -> Vector2;
    fn acceleration_mut(&mut self) -> &mut Vector2; 
    fn max_speed(&self) -> f32;
    fn max_speed_mut(&mut self) -> &mut f32;
    fn max_force(&self) -> f32;
    fn max_force_mut(&mut self) -> &mut f32;
    fn mass(&self) -> f32;
    fn mass_mut(&mut self) -> &mut f32;
}

struct Actor {
    velocity: Vector2,
    acceleration: Vector2,
    max_speed: f32,
    max_force: f32,
    mass: f32
}

impl Physics for Actor {
    fn velocity(&self) -> Vector2 {
        self.velocity
    }
    fn velocity_mut(&mut self) -> &mut Vector2 {
        &mut self.velocity
    }
    fn acceleration(&self) -> Vector2 {
        self.acceleration    
    }
    fn acceleration_mut(&mut self) -> &mut Vector2 {
        &mut self.acceleration
    }
    fn mass(&self) -> f32 {
        self.mass    
    }
    fn mass_mut(&mut self) -> &mut f32 {
        &mut self.mass    
    }
    fn max_speed(&self) -> f32 {
        self.max_speed    
    }
    fn max_speed_mut(&mut self) -> &mut f32 {
        &mut self.max_speed    
    }
    fn max_force(&self) -> f32 {
        self.max_force    
    }
    fn max_force_mut(&mut self) -> &mut f32 {
        &mut self.max_force
    }
}

fn main() {

}



Solution

  • The reason you can't immutably borrow while there is also a mutable borrow is because otherwise it's possible that you could use the mutable borrow to modify the underlying data represented by the immutable borrow. And since immutable borrows are (obviously) not supposed to change, the borrow checker can't take the risk (even if you "know" the mutable borrow won't affect the immutable one).

    In this case, I believe you can get around this by assigning self.mass() to a local before making the call to self.acceleration_mut()

    let mass = self.mass();
    let a = self.acceleration_mut();
    *a += *force / mass;
    

    Since mass is just a f32, it is copied on return so the immutable borrow will be done before the next line.