Parent <-> Child relation ownership problem

Hi, I'm just starting to learn Rust and I'm having some troubles understanding how ownership works in my case:

use std::ops::IndexMut;

// ================================================

struct Container {
    root: Option<ContainerItem>,
    items: Vec<ContainerItem>
}

impl Container {
    pub fn create_item(&mut self) -> usize {
        let item = create_test_item();
        let idx = self.items.len();
        self.items.push(item);
        return idx;
    }
    
    pub fn get_item(&mut self, index: usize) -> &mut ContainerItem {
        return self.items.index_mut(index);
    }

    pub fn new() -> Container {
        let mut x = Container {
            root: None,
            items: Vec::new()
        };
        
        x.root = Some(create_test_item());
        
        return x;
    }
}

// ================================================

struct ContainerItem {
    idx: usize,
    children: Vec<usize>,
    parent: usize,
    has_parent: bool
}

impl ContainerItem {
    pub fn add_child(&mut self, value: &mut ContainerItem) {
        value.parent = self.idx;
        value.has_parent = true;
        self.children.push(value.idx);
    }
}

// ================================================

fn create_test_item() -> ContainerItem {
    ContainerItem {
        idx: 1,
        children: Vec::new(),
        parent: 0,
        has_parent: false
    }
}

fn main() {
    let mut container = Container::new();
    
    let item_index = container.create_item();
    let item = container.get_item(item_index);
    
    if let Some(mut root) = container.root {
        root.add_child(item);
    }
}

I have a Container, with ContainerItems. Each ContainerItem can have children and also has a reference to its parent (both children and the parent are stored as their index in Container's items vec). This is all fine, adding items to the container works, but when I try to add a child to Container's root item (which is a ContainerItem), it throws errors:

error[E0503]: cannot use `container.root` because it was mutably borrowed
  --> src/main.rs:68:12
   |
66 |     let item = container.get_item(item_index);
   |                --------- borrow of `container` occurs here
67 |     
68 |     if let Some(mut root) = container.root {
   |            ^^^^^^^^^^^^^^ use of borrowed `container`
69 |         root.add_child(item);
   |                        ---- borrow later used here

error[E0505]: cannot move out of `container.root.0` because it is borrowed
  --> src/main.rs:68:17
   |
66 |     let item = container.get_item(item_index);
   |                --------- borrow of `container` occurs here
67 |     
68 |     if let Some(mut root) = container.root {
   |                 ^^^^^^^^ move out of `container.root.0` occurs here
69 |         root.add_child(item);
   |                        ---- borrow later used here

If I change the call to add_child to use for e.g. create_test_item() as the argument, then it works fine.

I assume this might be because Container's items field has ownership over all ContainerItems? But I'm passing it by reference to add_child so I think this shouldn't matter, since I'm not changing the owner?

1 Like

What happens is that Option won't implement interior mutability. So ContainerItem and Container are kept under the same ownership. Thus, trying to mutate root attribute after let Some, which doesn't expect mutation, will contradict the ownership.

Vec, on the other way, allows interior mutability of its items, being a kind of smart pointer.

In order to implement interior mutability pattern for a single item, which is root attribute, use std::cell::RefCell Check out the docs, which has pretty good explanation.

Also, Rust has std::rc::Rc to keep several references to a same object if you need.

… and additionally std::rc::Weak for cyclic references, e.g. when the parent needs a pointer from and to the child, otherwise the memory is never freed.

I'm not sure what you mean by this. There's no difference between Option<T> and Vec<T> with regard to the mutability of T.

2 Likes

You are right. Vec and Option behave in the same way, regarding the mutability. In order to mutate the items of the Vec, Container must be mutable. The same happens for the Option to ContainerItem, which can also change the ContainerItem.

The RefCell will allow changing the mutability state of the root reference. It would be possible to define Container as immutable, and change reference of root attribute.

use std::cell::RefCell;

struct Container {
    root: RefCell<ContainerItem>,
    items: Vec<ContainerItem>
}

impl Container {
    /* ... */
    pub fn new() -> Container {
        let x = Container {
            root: RefCell::new(create_test_item()),
            items: Vec::new()
        };
        
        return x;
    }
}
/* ... */
fn main() {
    let /* mut */ container = Container::new();
    container.root.borrow_mut().add_child(&mut create_test_item());
}

However, It won't solve your code.