Search code examples
methodsrustborrow-checkermutability

How can I use an object as a parameter of its own method in Rust?


I wrote the following code for a simple struct in Rust. This is just an example, it doesn't have much real logic:

struct Vec2 {
    x: f32,
    y: f32,
}

impl Vec2 {
    fn multiply(&mut self, other: &Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

I can create simple vectors and multiply a vector with another vector, but I encountered a problem when I tried to multiply a vector with itself: The compiler complains that I can't borrow that vector as mutable because it is also borrowed as immutable.

fn main() {
    let mut vec = Vec2 { x: 2.0, y: 2.3 };
    vec.multiply(&vec);
}
error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:5
   |
15 |     vec.multiply(&vec);
   |     ^^^^--------^----^
   |     |   |        |
   |     |   |        immutable borrow occurs here
   |     |   immutable borrow later used by call
   |     mutable borrow occurs here

This makes sense, but what would be the correct way to multiply such a vector with itself? And more importantly: for the general case where I need to modify a struct with its own method with that same struct as parameter.


Solution

  • I think you understand this already, but the reason that what you have now doesn't work is that a value cannot be mutably borrowed at the same time as an immutable borrow. To perform the method as is, you would need both the mutable borrow (in the form of &mut self) and the immutable borrow (in the form of other: &Vec2) at the same time. Since your method should work for any two members of Vec2, you'll always have two separate borrows: the compiler can't deduce that a single borrow would work for the case of multiplying the vector by itself.

    As for your question, you have several options, depending on the details of what you're trying to do.

    Option 1: cloning

    Simply add #[derive(Clone)] to your definition of Vec2 and you'll be able to clone an element of that type using the clone method.

    #[derive(Clone)]
    struct Vec2 {
        x: f32,
        y: f32,
    }
    
    impl Vec2 {
        fn multiply(&mut self, other: &Vec2) {
            self.x *= other.x;
            self.y *= other.y;
        }
    }
    
    fn main() {
        let mut vec = Vec2 { x: 2.0, y: 2.3 };
        vec.multiply(&vec.clone());
    }
    

    Option 2: copying

    If your type is this simple (just two floats), then you could reasonably also derive Copy. Then elements of the type are simply copied when fed to functions, rather than needing a reference. That means that we should change the signature of Vec2::multiply to take other as simply Vec2, rather than &Vec2 (this isn't strictly necessary, but taking a pointer is usually less efficient for Copy types).

    #[derive(Copy, Clone)]
    struct Vec2 {
        x: f32,
        y: f32,
    }
    
    impl Vec2 {
        fn multiply(&mut self, other: Vec2) {
            self.x *= other.x;
            self.y *= other.y;
        }
    }
    
    fn main() {
        let mut vec = Vec2 { x: 2.0, y: 2.3 };
        vec.multiply(vec);
    }
    

    Option 3: a dedicated method

    You could have a separate method called multiply_self or square (depending on your semantics) that just takes &mut self. This might be your best bet in the general case of modifying a struct using itself.

    struct Vec2 {
        x: f32,
        y: f32,
    }
    
    impl Vec2 {
        fn multiply_self(&mut self) {
            self.x *= self.x;
            self.y *= self.y;
        }
    }
    
    fn main() {
        let mut vec = Vec2 { x: 2.0, y: 2.3 };
        vec.multiply_self();
    }
    

    Option 4: return a new value

    You could have a method that doesn't take self mutably and instead returns a new Vec2. Then you could do vec = vec.multiply(&vec).

    struct Vec2 {
        x: f32,
        y: f32,
    }
    
    impl Vec2 {
        fn multiply(&self, other: &Vec2) -> Vec2 {
            Vec2 {
                x: self.x * other.x,
                y: self.y * other.y,
            }
        }
    }
    
    fn main() {
        let mut vec = Vec2 { x: 2.0, y: 2.3 };
        vec = vec.multiply(&vec)
    }
    

    There are probably lots of other ways to do this, but this is what comes to mind in this simple case. If you share more details about what your trying to do in general, I might be able to come up with more.