How to return and use multiple different immutable references of struct fields?

Here is some example code:

struct Bar {
    x: f32
}

struct Buz {
    data: [f32; 16]
}

struct Foo {
   foobar: Bar,
   foobuz: Buz
}

impl Foo {
   pub fn get_bar_mut(&mut self) -> &mut Bar {
      &mut self.foobar
   }

   pub fn get_buz(&self) -> &Buz {
      &self.foobuz
   }
}

fn do_stuff(foo: &mut Foo) {
    let buzzy = foo.get_buz();
    let barry = foo.get_bar_mut();
    if buzzy.data[0] == 0.0 {
       barry.x = 2.0;
    }
}

This code won't compile and I understand why. Given this context, I believe the code inside do_stuff and, in general, any code in patterned like that, is safe. Is there anyway to do something similar to what is done in do_stuff, or am I completely wrong and this code is indeed unsafe?

This is sometimes called an interprocedural conflict. The article is more general than the case of getters and setters, and goes over a few ways to work with it.

However, in this case, one way to avoid the problem is to just not use getters and setters, as they are particularly prone to creating this kind of compiler error. Or if you need them as a privacy or future-proofing boundary for your library, don't use them internally.

If you're handing out mutable references to part of your struct in particular (like get_bar_mut), the benefits of making the field private are much reduced.

4 Likes

The borrow checker checks function interfaces, not function bodies. The interface of get_bar_mut(&mut self) borrows all of self exclusively.

This behavior is intentional, because it's meant to give Foo an abstraction layer with a stable interface, so that other people's code won't break if you start using more fields in this method.