0

I have following code that can't be compiled:

struct A {
    x: i32,
}

impl A {
    fn add_assign(&mut self, other: &Self) {
        self.x += other.x;
    }

    fn double(&mut self) {
        self.add_assign(self);
    }
}

The error is:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/lib.rs:11:9
   |
11 |         self.add_assign(self);
   |         ^^^^^----------^----^
   |         |    |          |
   |         |    |          immutable borrow occurs here
   |         |    immutable borrow later used by call
   |         mutable borrow occurs here

How to pass self as the argument of add_assign? I have tried &self, *self, &*self without success.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Fomalhaut
  • 8,590
  • 8
  • 51
  • 95
  • 1
    What's the reasoning behind this? If I'm not mistaken, you want to first consume `self` by taking ownership of it then mutate `self` after you've already handed it over. What is your actual goal? – Mike Cluck Feb 05 '19 at 21:47
  • You do not need to pass a self argument. It's already "passed" when you call this method on a self instance – Arek C. Feb 05 '19 at 21:49
  • 1
    `add_assign` is supposed to use self and generally a different variable `other` to be added. But if I want to double, I want not to duplicate my code in `add_assign` and just pass `self` as the argument to be added. – Fomalhaut Feb 05 '19 at 21:53
  • 1
    @Fomalhaut You should make an add_assign (use the trait BTW), that take self, and the true type you want to add that will implement clone or copy. – Stargateur Feb 05 '19 at 22:49

1 Answers1

7

For the current version of the question

fn add_assign(&mut self, other: &Self)

Your request is impossible.

You cannot have a mutable reference and an immutable reference to the same value at the same time. This is a fundamental aspect of Rust.

Please re-read the rules of references.

See also:

For the first version of the question

fn add_assign(&mut self, other: Self)

Your request is impossible.

You need one instance of struct A to call the method on and another instance of A to pass as the argument. Your type does not implement Copy or Clone or provide any equivalent methods so there is no way to get a second instance.

Beyond that, there's no universal way to take a mutable reference to a value and get an owned value out of it.

See also:

Workarounds

If you implement Copy or Clone, then you can get a second value from the original and then call either of your versions.

If you implemented Copy:

  • (other: Self)

    self.add_assign(*self);
    
  • (other: &Self)

    let other = *self;
    self.add_assign(&other);
    

If only Clone:

  • (other: Self)

    self.add_assign(self.clone());
    
  • (other: &Self)

    self.add_assign(&self.clone());
    

You probably want to implement the AddAssign trait to provide syntax sugar. Assuming you've implemented Copy:

impl A {
    fn double(&mut self) {
        *self += *self;
    }
}

impl std::ops::AddAssign<Self> for A {
    fn add_assign(&mut self, other: Self) {
        self.x += other.x;
    }
}

Stargateur's comment may also be applicable, as i32 implements Copy:

impl A {
    fn double(&mut self) {
        *self += self.x;
    }
}

impl std::ops::AddAssign<i32> for A {
    fn add_assign(&mut self, other: i32) {
        self.x += other;
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • If I had `copy` or `clone` how the correct code would look like? Could you, please, provide a full working example of my `double` with `copy` or `clone` implementation? – Fomalhaut Feb 05 '19 at 21:57
  • With `clone` it doesn't work. The error is `immutable borrow occurs here`. – Fomalhaut Feb 05 '19 at 22:09
  • @Fomalhaut [please upgrade to Rust's 2018 Edition](https://stackoverflow.com/q/41187296/155423). – Shepmaster Feb 05 '19 at 22:11