1
votes

Prior question that this is not a duplicate of:

I have the following structs:

struct Foo {
    data: Vec<Bar>,
    a: usize,
}

struct Bar {
    b: usize
}

Inside impl Foo, I have the following methods:

fn example(&mut self, c: usize) {
    let baz = &mut self.data[c];
    let z = self.calc(c);
    baz.b = 42 + z;
}

fn calc(&self, x: usize) -> usize {
    self.a * x
}

Of course, the Rust compiler throws an error saying roughly "a mutable borrow occurs when you create baz, then you do an immutable borrow when you call self.calc and lastly you later use the mutable borrow when you assign to baz.a.

However, I'm accessing disjoint fields on the struct because calc never reads from data that is being written to through baz.

Is there a way to inform the Rust compiler of this?

2
Pretty sure there isn’t. In the example code, you can move the declaration of baz, and realistic workarounds are going to be similarly case-specific. Do you have an example closer to your actual code in that respect?Ry-♦

2 Answers

1
votes

The problem is the signature of Foo::calc, which takes &self as the receiver. This guarantees to calc that there are no mutable references to all of self, including any of its fields; that is, all of Foo is guaranteed to be immutable from the body of calc's point of view. This is not possible with the code as it is, because self.calc(c) requires to immutably borrow self (all of self) while a mutable borrow is still active.

The fact that calc never reads from data that is being written to baz is irrelevant. In Rust, all there is to know about a function or method is exposed in the signature; the compiler never looks "into" a function to figure out if it's a special case or not.

You can avoid the problem by not requiring calc to take &self as a receiver. For instance, by borrowing individual fields before the call (Self::calc(&self.a, c)) or, as in the example below, by not borrowing self at all:

impl Foo {
    fn example(&mut self, c: usize) {
        let baz = &mut self.data[c];
        // This avoids borrowing `self` in its entirety...
        let z = Self::calc(self.a, c);
        baz.b = 42 + z;
    }

    fn calc(a: usize, x: usize) -> usize {
        a * x
    }
}
0
votes

I ended up doing:

fn example(&mut self, c: usize) {
    let baz = &self.data[c];
    // some stuff that requires reading `baz`
    let z = self.calc(c);
    let baz = &mut self.data[c];
    baz.b = 42 + z;
}

fn calc(&self, x: usize) -> usize {
    self.a * x
}