Borrowing references to attributes in a struct

Solution 1:

It's a feature of the language. From the compiler point of view, there is no way for it to know that it's safe to call your set() function while a is borrowed via get().

Your get() function borrows b mutably, and returns a reference, thus b will remain borrowed until this reference goes out of scope.

You have several way of handling this:

  1. Separate your two fields into two different structs

  2. Move the code which needs to access both attribute inside a method of B

  3. Make your attributes public, you will thus be able to directly get references to them

  4. Compute the new value before setting it, like this:

    fn main() {
        let a = Box::new(A { i: 47 });
        let mut b = B { a: a, j: 1 };
        let newval = {
            let a_ref = b.get();
            foo(a_ref)
        };
        b.set(newval);
    }
    

Solution 2:

Expanding a bit on Levans' answer

Move the code which needs to access both attribute inside a method of B

That might look like this at a first pass:

impl B {
    fn do_thing(&mut self) {
        self.j = self.a.foo()
    }
}

However, this hard-codes the call to foo. You could also accept a closure to allow this to be more flexible:

impl B {
    fn update_j_with_a<F>(&mut self, f: F)
    where
        F: FnOnce(&mut A) -> i32,
    {
        self.j = f(&mut self.a)
    }
}

// ...

b.update_j_with_a(|a| a.foo())

Separate your two fields into two different structs

This is also applicable when you have borrowed two disjoint subsets of attributes. For example:

struct A {
    description: String,
    name: String,
    age: u8,
    money: i32,
}

impl A {
    fn update_description(&mut self) {
        let description = &mut self.description;
        *description = self.build_description()
        // cannot borrow `*self` as immutable because `self.description` is also borrowed as mutable
    }

    fn build_description(&self) -> String {
        format!(
            "{} is {} years old and has {} money",
            self.name,
            self.age,
            self.money
        )
    }
}

fn main() {}

Can be changed into

struct A {
    description: String,
    info: Info,
}

struct Info {
    name: String,
    age: u8,
    money: i32,
}

impl A {
    fn update_description(&mut self) {
        let description = &mut self.description;
        *description = self.info.build_description()
    }
}

impl Info {
    fn build_description(&self) -> String {
        format!(
            "{} is {} years old and has {} money",
            self.name,
            self.age,
            self.money
        )
    }
}

fn main() {}

You can combine these two steps (and I'd say that it's better practice) and move the method onto the inner struct.