Second mutable borrow: why does it occur here?

Hi all.

Please look at this example:


struct Info {
	thing: i32
}

enum StuffStatus {
	V1(Option<Info>),
	V2(Option<Info>),
	V3,
	V4(i32)
}

struct Stuff {
	status: StuffStatus,
	counter: usize
}

impl Stuff {

	fn do_stuff(&mut self) -> Result<(), String> {

		match &mut self.status {

			StuffStatus::V1(info) => {

				self.modify_something();

				let informations = info.take().unwrap();
				self.status = StuffStatus::V2(Some(informations));

				Ok(())

			}

			_ => Err("no.".into())

		}

	}

	fn modify_something(&mut self) {
		self.counter += 1;
	}

}

fn main() {

	let mut s = Stuff {
		status: StuffStatus::V1(Some(Info { thing: 1234 })),
		counter: 0
	};

	s.do_stuff();

}

I get this error:

23 |         match &mut self.status {
   |               ---------------- first mutable borrow occurs here
...
27 |                 self.modify_something();
   |                 ^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
28 |
29 |                 let informations = info.take().unwrap();
   |                                    ----------- first borrow later used here

I don't understand why calling the method modify_something() is a problem here. It uses its mutable reference &mut self, does its thing that remains all inside self, and that's it. Also, if I replace the line 27 with self.counter += 1 (i. e. the method content) the compiler compiles.

Maybe it is related to what I'm doing in lines 29 and 30? I'm just trying to change the variant from V1 to V2 while keeping the field content; I don't really need the Option inside these variants, but I can't find a better way.

Thanks for any explanation!

It's exactly what the compiler says. Info borrows self, because you obtained it by matching on &mut self.

But why it is different from replacing the self.modify_something() call with the function's content?

Do you have any suggestion to get out of this?

When you match on a mutable reference, the resulting info variable becomes a mutable reference to the thing. So in your case, info is a mutable reference that points inside self.status. Then, when you call self.modify_something(), this involves passing a mutable reference to all of Stuff to the modify_something method. Since a mutable reference to the status field overlaps with a mutable reference to the entire struct, this causes an error.

Replacing self.modify_something() with self.counter += 1 fixes it because by only accessing the counter field, you are no longer overlapping with self.status.

The crucial thing to note here is that the compiler does not "look inside" of modify_something when running the borrow checker on do_stuff. The borrow checker runs on each function separately, and it only looks at the function signatures of other functions when it checks one function.

3 Likes

Now I get it. If I change modify_something() in:

	fn modify_something(c: &mut usize) {
		*c += 1;
	}

And then I call it with:

Self::modify_something(&mut self.counter);

It works. These are fine details I'm still not used to think about :slight_smile:
Now the problem is that I don't know if I can do the same in the real case, but now I know better.
Thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.