Cannot move out from behind mutable reference to Option

Viewed 2086

I've got a struct:

struct Foo<'a> {
    parent: Option<&'a mut Foo<'a>>,
    value: i32,
}

impl<'a> Foo<'a> {
    fn bar(&mut self) {
        if let Some(&mut parent) = self.parent {
            parent.bar();
        } else {
            self.value = 1;
        }
    }
}

But I get the error:

error[E0507]: cannot move out of `*self.parent.0` which is behind a mutable reference
 --> src/lib.rs:8:36
  |
8 |         if let Some(&mut parent) = self.parent {
  |                          ------    ^^^^^^^^^^^ help: consider borrowing here: `&self.parent`
  |                          |
  |                          data moved here
  |                          move occurs because `parent` has type `Foo<'_>`, which does not implement the `Copy` trait

error[E0596]: cannot borrow `parent` as mutable, as it is not declared as mutable
 --> src/lib.rs:9:13
  |
8 |         if let Some(&mut parent) = self.parent {
  |                          ------ help: consider changing this to be mutable: `mut parent`
9 |             parent.bar();
  |             ^^^^^^ cannot borrow as mutable

I've tried many variations of that line and can't get it to work. How can I do this?

1 Answers

In your if let statement you are trying to destructure self.parent to obtain parent, and even obtain the Foo (itself, as a value) which is behind the stored reference.

You have to add an extra level of indirection to reference the &mut Foo that may or may not exist, without removing it from the Option if it exists.

if let Some(ref mut parent) = self.parent {

or

if let Some(parent) = self.parent.as_mut() {

The parent binding you obtain in both cases has type &mut &'a mut Foo<'a>, thus automatic-dereference will occur when calling parent.bar().

nb: I use as a reminder « ref on the left-hand-side of = is similar to & on the right-hand-side », but here we want to add the & inside the Option, thus the usage of .as_ref() or .as_mut() depending on the expected mutability.

Related