2

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() {

}


dylan
  • 289
  • 2
  • 11
  • 1
    Why do you need `&'static mut` anywhere, first of all? – Cerberus Jan 15 '21 at 05:29
  • I dont know. It complained about the Vector2 type not having a lifetime modifier so i gave it one. I have changed it to just have a &mut and it still seems to give the same error. I havent learned about lifetime specifiers yet so i guess i shouldnt use them. – dylan Jan 15 '21 at 05:35

2 Answers2

1

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.

Dan Simon
  • 12,891
  • 3
  • 49
  • 55
  • Thank you, this makes sense to me. So as a general rule i should borrow immutabley first then mutable borrow later? – dylan Jan 15 '21 at 07:08
  • yeah, often when you get errors like this it will usually be the case that you're borrowing something for longer than you need to. Other times you may have no choice but to copy something to avoid a long-lived borrow. Don't think there's one trick for every scenario though. – Dan Simon Jan 15 '21 at 07:25
  • This is alot of functions to implement for one type is there a way to do this for multiple types at once? – dylan Jan 15 '21 at 07:36
0

Since the "getters" return owned values, an alternative design is to use a local variable to compute the result, and assign it (via mutable reference) only at the end:

    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 mut a = self.acceleration(); // Line 12
        a += *force / self.mass(); //Line 13
        *self.acceleration_mut() = a;
        self.accelerate(truncate);
    }
    fn accelerate(&mut self, truncate: bool) {
        let mut v = self.velocity();
        v += self.acceleration();

        let max_speed = self.max_speed();

        if v.square_length() > max_speed * max_speed && truncate {
            v = v.normalize() * max_speed;
        }

        *self.velocity_mut() = v;
    }

Avoiding using local variables (e.g. if you assume that the underlying value is cloned, and that this is expensive), is tricky, because locking self via mutable reference for the whole length (or so) of a method is hard to manage.

Marcus
  • 5,104
  • 2
  • 28
  • 24