Cannot borrow `**...` as mutable, as it is behind a `&` reference

Viewed 38

I want to iterate through a vector of pointers. The vector is contained inside a struct, and the pointer points to the same struct, like a tree.

The vector is self.groups. I tried to loop over it, and call a (mutable) method, push_group(), but I got the error error[E0596]: cannot borrow **g as mutable, as it is behind a & reference.

I tried adding iter_mut(), but it didn't seem to do anything.

Am I doing something wrong ?

Playground : https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=14d911a460079a4eb747d6b10361fa60

The code for reference :

pub struct Group<'a> {
    pub name: String,
    pub groups: Vec<&'a Group<'a>>
}

impl<'a> Group<'a> {
    // Append a node under a node named "parent_name"
    pub fn push_group(&mut self, group: &'a Group<'a>, parent_name: &str) -> bool {
        if self.name == parent_name {
            self.groups.push(group);
            return true;
        }

        for g in self.groups.iter_mut() {
            let found = g.push_group(group, parent_name); // Error: cannot borrow as mutable
            if found {
                return found;
            }
        }

        false
    }
}

fn main() {
    let mut group = Group { name: "PATIENT_RESULT".to_string(), groups: vec![] };
    let other_group = Group { name: "OBSERVATION".to_string(), groups: vec![] };
    let _ = group.push_group(&other_group, "PATIENT_RESULT");
}
1 Answers

First, you need Group to own its children groups, not just have a reference to them (this is not strictly necessary, you can go with mutable references, but this is likely what you want):

#[derive(Debug)]
pub struct Group {
    pub name: String,
    pub groups: Vec<Group>,
}

impl Group {
    // Append a node under a node named "parent_name"
    pub fn push_group(&mut self, group: Group, parent_name: &str) -> bool {
        if self.name == parent_name {
            self.groups.push(group);
            return true;
        }

        for g in self.groups.iter_mut() {
            let found = g.push_group(group, parent_name);
            if found {
                return found;
            }
        }

        false
    }
}

But this will still error with:

error[E0382]: use of moved value: `group`
  --> src/main.rs:15:38
   |
8  |     pub fn push_group(&mut self, group: Group, parent_name: &str) -> bool {
   |                                  ----- move occurs because `group` has type `Group`, which does not implement the `Copy` trait
...
15 |             let found = g.push_group(group, parent_name);
   |                                      ^^^^^ value moved here, in previous iteration of loop

The problem is that Rust thinks that you may push the same group twice, but a group needs to have only one owner. This cannot happen because as soon as we push the group we return true and exit, but Rust doesn't know that. To make Rust happy, a common workaround is to use Option with .take().unwrap():

pub fn push_group(&mut self, group: Group, parent_name: &str) -> bool {
    fn push_impl(this: &mut Group, group: &mut Option<Group>, parent_name: &str) -> bool {
        if this.name == parent_name {
            this.groups.push(group.take().unwrap());
            return true;
        }

        for g in this.groups.iter_mut() {
            let found = push_impl(g, group, parent_name);
            if found {
                return found;
            }
        }
        
        false
    }
    push_impl(self, &mut Some(group), parent_name)
}
Related