Are there any differences between returning &mut self.field and creating them directly with &mut struct.field?

struct Example {
    a: usize,
    b: usize,
}

impl Example {
    fn a(&mut self) -> &mut usize {
        &mut self.a
    }
    fn b(&mut self) -> &mut usize {
        &mut self.b
    }
}

fn main() {
    let mut s = Example { 
        a: 1,
        b: 2, 
    };

    // References created by returning &mut self.a and &mut self.b
    // from methods
    let x = s.a();
    let y = s.b();

    // References create by accessing the field directly
    let x = &mut s.a;
    let y = &mut s.b;

    // Ensure x/y aren't dropped due to non-lexical scope 
    x;
    y;
}

As you can see I'm creating references to two different fields. The first time I'm doing it directly, the second time I'm doing it via a methods returning &mut self.field.

My question is, are there any subtle differences I'm missing or is it okay to consider the first set of x/y identical to the second set?

When you use a method to create the borrow, the entire struct gets borrowed, whereas when you reference the field directly, only the field is borrowed.

1 Like

Due to what @alice said, you can't borrow both fields mutably via individual getters at the same time. Try commenting out the "References create by accessing the field directly" block and you'll get an error about borrowing s mutably more than once. (You don't get an error now because you don't actually use your first x and y, so it makes the first borrow end before the second borrow.)

(Using setters and getters when you're within the struct privacy boundary usually isn't idiomatic, in part due to these borrowing semantics.)

I see. That all makes sense. I understand I'm borrowing the entirety of the struct when using a method.

My thinking is I'm receiving a reference to self, using it to create a direct reference to a field which I return. The reference to self then gets dropped at the end of the method leaving only the direct reference to the field I returned. Obviously my understanding is flawed, the self reference is obviously somehow persisting beyond the end of the method.

The Rust compiler generally guarantees that changing the body of a function cannot cause other code using that function to stop compiling. Only changing the function signature can cause that. Therefore, since the body of a could be modified to &mut self.b without changing the signature, the main function must be written such that it would still be correct even if such a change is made.