How to reference self in a spawned child object

What I basically want to implement — a parent, that can spawn a child with reference to self. And if child gets mut — reference also gets mut.
If I create child object from scratch outside of parent — I can have parent in Rc<> and pass to child Rc copy. Which look good for me until I want to have elegant API (pseudo):

let parent = Parent::new();
parent.create_child(id="api test") \\ mut method
let child = parent.create_child(id="first child");  \\ mut method, but non-mut reference
let child2 = parent.create_child(id="second child"); \\ non mut
child2.change_id("second bud") \\ error — mut reference of parent

Since I make an high-level wrap of Cpp FFI to non-object API, I can avoid all the errors and warnings. But I would like to have something like this: when multiple child objects can be spawned by parent without borowing parent as &mut. Is it possible?

As far as I can tell, you probably want Rc<RefCell<T>> or Arc<Mutex<T>> or similar. Read more here.

You may get a more precise response if you share some actual code and errors.

Rc works great in case child is constructed outside the parent. e.g.

let parent = Rc::new(Parent::new());
let child = Child::new(parent.copy());

But how to make child in the method of parent?

impl Parent{
    fn make_child(&self) -> Child{
        Child{parent:self}
    }
}

arbitrary_self_types is the unstable feature you want here: Tracking issue for `arbitrary_self_types` · Issue #44874 · rust-lang/rust · GitHub

It's usable today if you use UFCS instead of method call syntax.

impl Parent{
    fn make_child_with_n(self: &Rc<RefCell<Self>>, n: i32) -> Child{
        drop(n); // pretend we do something useful with it
        Child{parent: Rc::downgrade(Rc::clone(self))}
    }
}
fn main() {
  // ...
  let child = Parent::make_child_with_n(&parent, 3);
}

See also Rc::new_cyclic and rc::Weak.

1 Like

Interesting! Thank You, I'll try now!

P.S. real-world example. I'm sorry, it will take a while to make clean one-module example...

If you want Track.name to be semantically settable without taking a &mut to the parent node, you can also consider wrapping only the name field in a RefCell.

struct Track {
  project: Weak<Project>,
  pointer: *mut MediaTrack,
  name: RefCell<Option<String>>,
}
impl Track {
  fn set_name(&self, name: impl Into<String>) {
    *self.name.borrow_mut() = Some(name.into);
  }
  fn remove_name(&self) { *self.name.borrow_mut() = None; }
  // ! Note change in function signature
  fn get_name(&self) -> Ref<'_, Option<&str>> { ... }
}

However, this gets complicated because you have long lived dynamic borrows being returned from get_name -- if any of those are standing when you call set_name, then borrow_mut will panic!

More complicated options include RefCell<Option<Rc<str>>> (solves Ref problems by returning Rc<str> from get_name) and other reference-counted string types, like the bytes crate bytes::Bytes.

1 Like

Thank You so much for the both examples! I need a bit of time to experiment with them. But, actually, I have an opposite problem: I do not have a error in place where I would like to see it (Track::set_name(& mut self) should also make ref to track.project mutable).

Or I have (currently) an option to carry &mut Project inside track, which will not allow having multiple immutable tracks in one scope and even have problems with iterations through them.

I'll write when got noticeable result)

OK, this code behaves exactly like I want from it. But it not produces errors by rustcheck, which was my original wish. And, what bothers me more — that it makes the mutable behavior implicit, but for the crate I would like to see it explicit...

use std::rc::Rc;

struct Child {
    parent: Rc<Parent>,
}
impl Child {
    fn new(parent: Rc<Parent>) -> Self {
        Self { parent }
    }

    fn non_mut(&self) -> bool {
        self.parent.is_alive()
    }

    fn mut_f(&mut self, state: bool) {
        let parent = Rc::get_mut(&mut self.parent).unwrap();
        parent.make_alive(state)
    }
}

struct Parent {
    alive: bool,
}

impl Parent {
    fn new() -> Self {
        Self { alive: true }
    }
    fn make_child(self: &mut Rc<Self>) -> Child {
        Child::new(self.clone())
    }
    fn is_alive(&self) -> bool {
        self.alive
    }
    fn make_alive(&mut self, state: bool) {
        self.alive = state
    }
}

#[test]
fn childs() {
    let mut parent = Rc::new(Parent::new());
    let mut ch1 = parent.make_child();
    // let ch2 = parent.make_child(); // will error if uncommented
    drop(parent); // will error if commented
    println!("{}", ch1.non_mut());
    ch1.mut_f(false);
    // println!("{}", ch2.non_mut());  // will error if uncommented
}

OK. I think, I finally found what I was looking for:

use std::ops::{Deref, DerefMut};

struct Child<T: Deref<Target = Parent>> {
    parent: T,
}
impl<T: Deref<Target = Parent>> Child<T> {
    fn new(parent: T) -> Self {
        Self { parent }
    }

    fn is_alive(&self) -> bool {
        self.parent.is_alive()
    }
}
impl<T: DerefMut<Target = Parent>> Child<T> {
    fn set_alive(&mut self, value: bool) {
        self.parent.make_alive(value);
    }
}

struct Parent {
    alive: bool,
}

impl Parent {
    fn new() -> Self {
        Self { alive: true }
    }
    fn make_child(self: &mut Self) -> Child<&Parent> {
        Child::new(self)
    }
    fn make_child_mut(self: &mut Self) -> Child<&mut Parent> {
        Child::new(self)
    }
    fn is_alive(&self) -> bool {
        self.alive
    }
    fn make_alive(&mut self, state: bool) {
        self.alive = state
    }
}

#[test]
fn childs() {
    let mut parent = Parent::new();
    let ch2 = parent.make_child();
    let mut ch1 = parent.make_child_mut();
    println!("{}", ch1.is_alive());
    ch1.set_alive(false);
    // println!("{}", ch2.is_alive()); // will not compille if uncommented.
}

UPD: Ahh, no. Now even immutable version of child from make_child borrows parent as mut until child drops.

As an intermediate stable solution I've split creation of child (track) and getting it.
Works good and statically evaluates.
изображение

Only thing I would like to solve — making Iterator over &mut versions.

Later I faced several problems with naive generic implementation. So, I've sketched the situation in one-module test, and found solution, that seems to me quite elegant: generic_mutability_test - Rust

env_logger::init();
let mut root = Root::new();
let window1 = root.make_child();
// Err: cannot borrow `root` as mutable more than once at a time
// let window2 = root.make_child(); // try to uncomment

let w1_id = window1.get_id(); // Drop window1 and finish &mut borrow of Root
debug!("{}", w1_id);
let id2 = root.make_child().get_id();
let _window1 = root.get_child(w1_id).unwrap();
let _window2 = root.get_child(id2).unwrap(); // OK!

// Err: no method named `make_button` found for struct `Window<'_,
// test::Immutable>` in the current scope. The method was found for
// `Window<'a, test::Mutable>`
// _window1.make_button()

let mut window1 = root.get_child_mut(w1_id).unwrap();
let button = window1.make_button();
let b_id = button.get_id();
let mut frame = window1.make_frame();
let fr_b_id = frame.make_button().get_id();
let f_id = frame.get_id();
// Err: cannot borrow `window1` as mutable more than once at a time
// debug!("button text: {}", button.get_text());

// Err: no method named `get_button` found for struct
// `Window<'_, test::Mutable>` in the current scope
// the method was found for - `Window<'a, test::Immutable>`
// let button = window1.get_button(b_id);
let window1 = root.get_child(w1_id).unwrap();
let frame = window1.get_frame(f_id).unwrap();
let w_b = window1.get_button(b_id).unwrap();
let fr_b = frame.get_button(fr_b_id).unwrap();

debug!("is window button clicked: {}", w_b.is_clicked());
debug!("is frame button clicked: {}", fr_b.is_clicked());
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.