2

I know in Rust, there can't be multiple reference to one object if one of them is mutable. So when I want to pass self as a reference inside a loop that is borrowing a member already, it wouldn't compile.

I have a trait function that takes a reference to bar:

trait Foo {
    fn perform(&mut self, bar: &Bar);
}

In Bar, I have a vector of Foos:

struct Bar {
    foos: Vec<Box<dyn Foo>>,
}

The following code would not compile because it's borrowing self twice

impl Bar {
    fn do_it(&mut self) {
        for foo in &mut self.foos {
            foo.perform(self);
        }
    }
}
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src/lib.rs:12:25
   |
11 |         for foo in &mut self.foos {
   |                    --------------
   |                    |
   |                    mutable borrow occurs here
   |                    mutable borrow later used here
12 |             foo.perform(self);
   |                         ^^^^ immutable borrow occurs here

Is there nice way to do it so Foo::perform() can use the references of both self and bar without the compiler complaining?

Edit

A couple ways i thought of to get around this:

  1. Use pointer
fn do_it(&mut self) {
    let ptr = self as *const Bar;
    for foo in &mut self.foos {
        foo.perform(ptr);
    }
}
  1. Take away from the vector then put it back after
fn do_it(&mut self) {
    let len = self.foos.len();
    for i in 0..len {
         let foo = self.foos.swap_remove(i);
         foo.perform(self);
         self.foos.push(foo);
         self.foos.swap(i, len-1);
    }
}

Both ways are not rusty, though. I'm still looking for a way to make perform() get the fields from Bar without taking its reference.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Rio6
  • 188
  • 2
  • 7
  • No. Your `perform` method is allowed to empty out the `foos` vector, leading to memory unsafety. The compiler cannot allow this. Make finer-grained functions or types to be explicit that `perform` cannot possibly alter `foos`. – Shepmaster Jun 25 '19 at 00:23
  • Shouldn't it not be allowed to do that since it takes bar as immutable reference? – Rio6 Jun 25 '19 at 00:33
  • Sorry, I got it mixed up. It's disallowed because given `&Bar`, you could get a second reference to `self`. For example, `self.foos[0].perform(self)` / `fn perform(&mut self, bar: &Bar) { bar.foos[0] }`. This would allow a mutable and immutable reference to alias. – Shepmaster Jun 25 '19 at 13:50

0 Answers0