2

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
lolad
  • 321
  • 6
  • 13
  • 3
    FYI: `&'a mut Foo<'a>` is virtually always a mistake because it means the `Foo` is borrowed for its entire lifetime. If you have the same thing in real life code, you're probably going to have a bad time. See, for instance, [Why does linking lifetimes matter only with mutable references?](/q/32165917/3650362) – trent Oct 14 '20 at 18:59

1 Answers1

4

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.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • 1
    It's not necessary here, but `Option::as_deref_mut` can sometimes make code read more cleanly. In this case, by matching against an `Option<&mut Foo<'a>>` instead of `Option<&mut &'a mut Foo<'a>>`. – trent Oct 14 '20 at 18:54