Creating a struct with a parent reference

I want to create 2 structs with the following signatures

struct Parent {
    child: Child<'static>,
    more_children: Vec<Child<'static>>
}

struct Child<'a> {
    parent: &'a Parent,
    ...more_fields
}

I can't create the child until I have the parent, and I can't create the parent until I have the child. Once I've got them both I'm good to go.

My current plan is to use mem::uninitialized for the child, then once I've created the child from the parent, replace it with the correct value using ptr::write, but I need to do things like catch_unwind to make it safe. Is there a better way to do this?

1 Like

To have guaranteed safety you have to use Rc. The borrow checker doesn't support parent-child mutual references, so if you trick it into making such structure, it wont be able to uphold safety.

If you want to do it unsafely, use *mut Parent

1 Like

Thanks!

EDIT: Do I still need unsafe to create this: could you explain how to if not?

A raw ptr version:

struct Parent {
    child: Child,
}

struct Child {
    parent: *const Parent,
}

fn main() {
    let mut child = Child {
        parent: std::ptr::null(),
    };
    let parent = Parent { child };
    child.parent = &parent;
}

A Rc version:

use std::rc::Rc;

// All Parent state that needs to be shared with Child goes here
struct Inner;

struct Parent {
    child: Child,
    inner: Rc<Inner>,
}

struct Child {
    parent: Rc<Inner>, // or Weak<Inner> if that's desirable
}

fn main() {
    let inner = Rc::new(Inner);
    let child = Child {parent: Rc::clone(&inner)};
    let parent = Parent {child, inner};
}
5 Likes

I've resolved this issue before by distinguishing between owned Child data and a more-functional DynamicChild that has the reference to a parent. So:

struct Parent {
  pub children: Vec<Child>,
}

impl Parent {
 fn get_child(&'a self, name) -> DynamicChild<'a> {
   DynamicChild { parent: self, child: ...}
 }
}

struct Child {
 a: u64,
 b: String,
}

struct DynamicChild<'a> {
 pub data: &'a Child,
 pub parent: &'a Parent,
}

Now we can implement our useful functions that care about having a reference to Parent on DynamicChild

impl<'a> DynamicChild<'a> {

 fn do_thing_with_parent(&self) -> usize {
  self.parent.children.len()
 }
}

This is mostly just a convenience mechanism to avoid having to pass &Parent into most methods that you want to implement on Child -- just a way to bundle up &Parent a &Child temporarily.

Apologies for any errors in my code, I'm typing this out from memory, and it's been a few months since I've done any Rust!

6 Likes

Thanks for all the help! I think I may do something that is a mixture of both answers. I think at this point I want to keep things safe - I can iterate with unsafety later.