ruststruct

Circular Reference Construction in Rust


A question about Rust circular references. In C++ I have structures along the lines of...

#include <memory>
#include <iostream>

struct Parent;
struct Child;

struct Parent {
  std::shared_ptr<const Child> child;
};

struct Child {
  std::weak_ptr<const Parent> parent;
};


auto makeStructs() -> std::shared_ptr<const Parent>
{
  auto parent = std::make_shared<Parent>();
  parent->child = std::make_shared<const Child>(parent);
  return parent;
}

After construction both parent and child are always const and never modified, though other structs may use the shared pointers to both parent and child.

To do the same in Rust it looks like I need to do the below. I need to make the parent before the child and so have it mutable via a RefCell as I can only set the child field after I’ve made the child struct, which has to refer back to the parent, so the child needs to be optional in the parent class. This strikes me as both ugly and inefficient as the Option and RefCell wrappers are redundant after the function returns, so imposing the runtime cost of Option and RefCell, as well ongoing code complexity to handle them. Is there a lighter way to do this?

use std::rc::{Rc, Weak};
use std::cell::RefCell;

pub struct Parent {
    child : Option<Rc<Child>>
}

pub struct Child {
    parent: Weak<RefCell<Parent>>
}

pub fn make_structs() -> Rc<RefCell<Parent>>
{
    let parent = Rc::new(RefCell::new(Parent{child: None}));
    parent.borrow_mut().child = Some(Rc::new(Child {parent : Rc::downgrade(&parent)}));
    
    return parent;
}

That is, what I'd like is along the lines of...

use std::rc::{Rc, Weak};

pub struct Parent {
    child : Rc<Child>
}

pub struct Child {
    parent: Weak<Parent>
}

pub fn make_structs() -> Rc<Parent>
{
   // ?????
}

Solution

  • You can do it with new_cyclic which allows you to have a Weak pointer to a value that doesn't exist yet:

    use std::rc::{Rc, Weak};
    
    pub struct Parent {
        child : Rc<Child>
    }
    
    pub struct Child {
        parent: Weak<Parent>
    }
    
    pub fn make_structs() -> Rc<Parent>
    {
        Rc::new_cyclic (|parent| Parent { 
            child: Rc::new (Child { parent: parent.clone() })
        })
    }
    

    Playground