Skip to content

Reference cycle graph potentially misleading? #1855

@ElliotSis

Description

@ElliotSis

Hi,

As a beginner, I recently got confused when reading the chapter about reference cycles:
https://doc.rust-lang.org/stable/book/ch15-06-reference-cycles.html

fn main() {
    let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));

    println!("a initial rc count = {}", Rc::strong_count(&a));
    println!("a next item = {:?}", a.tail());

    let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));

    println!("a rc count after b creation = {}", Rc::strong_count(&a));
    println!("b initial rc count = {}", Rc::strong_count(&b));
    println!("b next item = {:?}", b.tail());

    if let Some(link) = a.tail() {
        *link.borrow_mut() = Rc::clone(&b);
    }

    println!("b rc count after changing a = {}", Rc::strong_count(&b));
    println!("a rc count after changing a = {}", Rc::strong_count(&a));

    // Uncomment the next line to see that we have a cycle;
    // it will overflow the stack
    // println!("a next item = {:?}", a.tail());
}

cycle

I found the representation of this cycle to be a bit misleading considering that it is later mentioned that the variables a and b are dropped at the end of the scope.
However, if we base ourselves solely on this graph, if somehow a and b are dropped then we shouldn't be left with a cycle?

My understanding is that a and b are Rc; and these variables are indeed dropped (i.e. the count of the managing shared pointers is decreased from 2 to 1).
However, the values these Rc were pointing to are not cleaned up since what is essentially left over is trapped in a cycle between two Rc of count 1.
I could be wrong, but I had something closer to this representation in my mind by looking at the code:

cycle_other

I went on #rust-beginners because I thought I was missing something, and the person I ended up talking to seemed to agree that there was a potential issue here.

PS: The .dot file for this cycle graph seems to be missing, I created the above graph with the following .dot file.

digraph {
    node[shape=record];
    rankdir=LR;
    
    l1[label="{<data> 5| <next>}"];
    l2[label="{<data> 10| <next>}"];
    
    {node[shape=point height=0] invisible_start invisible_end}

    a -> l1:n;
    b -> l2:n;
    invisible_start:n -> l1[arrowtail=none];
    invisible_start:s -> invisible_end:s[dir=none];
    l1:next:c -> l2:data;
    l2:next:c -> invisible_end:n[arrowhead=none];
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions