rustselfreference-countingcyclic-referencerefcell

how to clone an Rc from a method that takes in &self


I have a struct Region which contains [Option<Rc<RefCell<Region>>>; 2] as its subregions and Option<Weak<RefCell<Region>>> as its container. I am writing a method to insert a new Region between self and self.container, which requires me to clone the Rc to self to include in the newly created Region as one of its subregions. When self.container is Some this is possible because I can walk up to the container and then clone its subregion that matches self, but when self has None as its container, I do not know how to get a clone of the Rc of self from the &mut self that is passed into the method.

Here is some code to make it clearer.

struct Region {
    subregions: [Option<Rc<RefCell<Region>>>; 2],
    container: Option<Weak<RefCell<Region>>>,
}

impl Region {
    fn clone_from_container(&self) -> Option<Rc<RefCell<Region>>> {
        Some(Rc::clone(&self.container
                       .as_ref()?
                       .upgrade()
                       .unwrap()
                       .borrow()
                       .subregions[self.from()]
                       .as_ref()
                       .unwrap()))
    }

    pub fn insert_above(&mut self) -> Rc<RefCell<Region>> {
        let new = Rc::new(RefCell::new(Region {
            subregion: [self.clone_from_container(),None],
            container: self.container.clone(),
        }));
        self.replace(new.clone());
        self.container = Some(Rc::downgrade(&new.clone()));
        new
    }
}

In my actual code there are a few more functions, but these are just aliases for long method chains to unbox the Rcs and RefCells to allow me to actually get at the data. I've tried to keep it to the important details only.

The methods I've excluded, Region::replace and Region::from are pretty simple and their implementation don't matter. replace walks up to the container and replaces the reference to self with the new region (very similar to clone_from_container but to mutate the Region), and from simply finds the index of the Region within its container's subregions (0 or 1)

When setting the subregion of new within, I would like to be able to say something like self.clone_from_container().or(Rc::clone(self)), but obviously this isn't possible given the type that self is passed as.

I figure it may just be necessary to have this be a static factory function, rather than an instance method, so we can pass self with type Rc<RefCell<Region>>, but I also thought it may be possible to use Rc::new_cyclic; however, that function is somewhat confusing and I'm not quite sure how to employ it.


Solution

  • To simplify, I'm gonna say Rc<Region> instead of Rc<RefCell<Region>>

    If I understood correctly, this is your situation:

    let region = Region::new()
    let rc = Rc::new(region)
    

    And you have a method such as:

    impl Region {
        fn my_function(&mut self) {
            // Here you want to somehow get a reference to `rc` (the Rc that contains this region)
        }
    }
    

    This is not possible, because Region doesn't know that it is inside an Rc. You have to somehow tell it that it is inside an Rc.

    One possible solution is to not use self. For example:

    impl Region {
        fn my_function(value: Rc<Self>) {
            // Now you have access to `rc`
        }
    }
    

    Another option, as PitaJ suggested, is to make a trait for Rc<Region>, in that case, self would be Rc<Region>, so you'd have access to Rc.