Some questions about reference cycle

In rust book example, Reference Cycles Can Leak Memory - The Rust Programming Language

Questions:

a) why the last println would overflow the stack?

b) Why match return reference ( &RefCell)?

    fn tail(&self) -> Option<&RefCell<Rc<List>>> {
        match self {
            Cons(_, item) => Some(item),
            Nil => None,
        }
    }

c) why reference cycle here? Does a and b would get deallocated when they are out of scope?

d) why Debug prints value field of RefCell and ignore the other field borrow?

pub struct RefCell<T: ?Sized> {
    borrow: Cell<BorrowFlag>,
    value: UnsafeCell<T>,
}

a points to b and b points back to a. If you try to print a, it will print its contents, then go on to print b. Which in turn prints the wrapped value of b, and goes on to print a. Hence, infinite recursion happens.

This is a quite confusing feature of not-so-recent Rust. You can match on a reference-typed expression (self here) and then pretend it's not a reference (you can write Cons and Nil in the match arms instead of &Cons and &Nil), but then the inner values are implicitly turned into references again. This is called "pattern matching ergonomics", you can read more about it here.

If a and b mutually point to each other, then they both have a reference count of 2: one from the actual variables that you are holding, and another from mutually referencing each other. When you drop the variables, both reference counts drop to 1, not to 0, hence, nothing is deallocated.

Because that's how it's implemented in std. Probably the reasoning is that it's conceptually a wrapper type, and you are only interested in the wrapped value rather than the full borrow state.

5 Likes