Search code examples
data-structuresrustinterior-mutability

Having Rc<T>s in one module and Rc<RefCell<T>>s in another referencing the same data


I have a data structure like this:

mod nodes {
    struct Node {
        predecessors: Vec<Rc<Node>>
    }
}

I obviously don't need mutability of the nodes in that module. I also don't have cyclic references here.

But I have another module, which contains nodes, where I need mutability:

pub mod network {
    mod nodes {...}

    struct Network {
        nodes: Vec<Vec<Rc<RefCell<Node>>>>
    }
}

I just can't figure out a way to create the Rc<Node>s for the nodes themselves together with the Rc<RefCell<Node>>s for the Network.

Do you have any idea on how to implement the immutability in the nodes module next to the mutable nodes in the network module?

Or is there no other way than to declare the references to the predecessor nodes inside the Node struct as Rc<RefCell<Node>>, even though I don't need mutability here (I would like to avoid that)?


Solution

  • Rust has single ownership. If a Node is owned by a RefCell, it cannot also be owned by a different Rc at the same time.

    When a Node can be modified in one place via RefCell, these changes cannot be allowed to show up elsewhere in Rc<Node>. This would violate immutability of Rc<Node>.

    Also note that Node is a struct value, not a pointer to an object. There's no extra layer of indirection that would even make it possible to share it between RefCell and other Rc. Its data is copied and inlined in its container.

    • Consider using Rc<RefCell<Node>> in both places. If the same data is used in both places, it has to uphold all guarantees in both places, even if you're not taking advantage of that.

    • If you're not using both types at the same time, you can convert from one type to another by deconstructing it (into_inner, try_unwrap) and constructing the other type again. This conversion is not free, because physical memory layout of these types is different.

    • If you only sometimes need mutability, Rc has make_mut that works without RefCell. And RefCell has a zero-cost get_mut if you already have a mutable access to it, so you might be able to reduce their overhead in specific cases.