0

Why the following code compiles:

struct U2 { index: usize }
struct U { ulist: Vec<U2> }
struct V2 { value: u8 }
struct V { vlist: Vec<V2> }
struct UV { u: U, v: V }

impl V2 {
    fn bar(&mut self) {
        self.value = 42;
    }
}
impl UV {
    fn foo(&mut self) {
        for u2 in &self.u.ulist {
            let v2 = &mut self.v.vlist[u2.index];
            v2.bar();
        }
    }
}

Whereas the following one, exactly the same as the previous code except for the line self.v.vlist[u2.index] which has been extracted to a function, does not:

struct A2 { index: usize }
struct A { alist: Vec<A2> }
struct B2 { value: u8 }
struct B { blist: Vec<B2> }
struct AB { a: A, b: B }

impl B2 {
    fn bar(&mut self) {
        self.value = 42;
    }
}
impl AB {
    // New function
    fn get_b(&mut self, a2: &A2) -> &mut B2 {
        &mut self.b.blist[a2.index]
    }
    fn foo(&mut self) {
        for a2 in &self.a.alist {
            let b2 = self.get_b(a2);
            // error[E0502]: cannot borrow `*self` as mutable
            // because it is also borrowed as immutable
            b2.bar();
        }
    }
}

I'm new to Rust, so excuse me in advance for my bliss ignorance, but this difference makes me mad, and I cannot see how to code this cleanly.


Edit

I've solved my issue on borrowing only part of self (self.b), by using the following function:

// ... (snip) ...
impl AB {
    fn get_b<'a, 'b>(b: &'b mut B, a2: &'a A2) -> &'b mut B2 {
        &mut b.blist[a2.index]
    }
    fn foo(&mut self) {
        for a2 in &self.a.alist {
            let b2 = AB::get_b(&mut self.b, a2);
            b2.bar();
        }
    }
}

Is this proper rust?

Laurent Grégoire
  • 4,006
  • 29
  • 52
  • 1
    The borrow checker does not see through functions. In many languages, "extracting a function" is considered an idempotent refactoring. This is not generally true in Rust. – Wesley Wiser Jul 16 '19 at 13:40
  • Consider what the signature of `get_b()` says with explicit lifetimes: `fn get_b(&'a mut self, a2: &'b A2) -> &'a mut B2`. The lifetime of `&mut B2` is the tied to that of `&mut self` which means you can't use `self` while the `&mut B2` reference exists. – Wesley Wiser Jul 16 '19 at 13:43

0 Answers0