We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Smart Pointers and Interior Mutability step 18 of 26
`Rc::new_cyclic`: self-reference in one step
Build entries that each hold a Weak handle back to themselves.
pub fn registry(names: Vec<String>) -> Vec<(String, bool, usize)>
pub fn cyclic_facts(names: Vec<String>) -> (Vec<bool>, Vec<String>)
An Entry holds its name, a me: Weak<Entry> pointing at itself, a
seen_in_closure: bool recording whether upgrade() worked during
construction, and a handle to a shared drop log.
registry returns, per name in order:
(name, whether me.upgrade() succeeds now, the entry's strong count).
cyclic_facts returns (the seen_in_closure flags, the names in the order their destructors ran once the whole registry was released).
Read this before you write the count: a live Rc returned by upgrade()
is a strong reference, and the temporary it lives in survives to the end of
the statement. Measure Rc::strong_count before you upgrade, or you will
report one more than you meant to.
The chicken and the egg
You want a value that contains a handle to itself. To make the handle you need
the Rc. To make the Rc you need the value. Neither can go first.
The way most people solve it — and the starter is written this way on purpose — is to build it with a placeholder and patch it up afterwards:
let entry = Rc::new(Entry { name, me: Weak::new(), … });
entry.me = Rc::downgrade(&entry);
error[E0594]: cannot assign to data in an `Rc`
| entry.me = Rc::downgrade(&entry);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign
= help: trait `DerefMut` is required to modify through a dereference,
but it is not implemented for `Rc<Entry>`
Of course. Rc hands out &T, and it always will. The workaround people
reach for next is to make the field RefCell<Weak<Entry>> — which is exactly
what the previous problem’s tree had to do, for exactly this reason. It works,
and it costs you three things: a RefCell you never needed, a field that is
optional in the type but mandatory in the invariant, and a second
construction pass you can forget to run.
Rc::new_cyclic
Rc::new_cyclic(|me: &Weak<Entry>| Entry {
name,
me: me.clone(),
…
})
The closure is handed a Weak to the allocation before the value is
written into it, and returns the value. One allocation, one step, and the
field can be a plain Weak<Entry> with no Option and no RefCell — so
“I forgot to set the back-pointer” stops being a possible bug, because the
type will not let you construct an entry without one.
The trap that is worth knowing
Inside the closure, calling me.upgrade() returns None.
It has to. The allocation exists, and the weak counter is live — that is how
you are holding a Weak at all — but the value has not been written yet, so
there is nothing to hand out a strong reference to. Upgrading would produce
an Rc to uninitialised memory.
This is documented, and it still catches people, because the natural reading
of “here is a handle to the thing” is that the thing is there. seen_in_closure
in this problem is false for every entry, always. After construction returns,
the same weak handle upgrades happily.
So: inside the closure, use the Weak — do not dereference it. Store it,
clone it, hand it to a child. Do not try to read through it.
What it does not solve
new_cyclic builds self-reference: one allocation whose value refers back
to itself. It cannot build mutual cycles — two nodes each pointing at the
other — because the second node does not exist when the first closure runs.
For those you are back to RefCell<Weak<T>> and a patch-up pass, and you
should ask hard whether the mutual link needs to be there at all.
And it does not leak
Note what the drop log says: every entry’s destructor runs. A self-reference
through Weak is not a cycle in the strong graph, so the counts still reach
zero. Had the field been me: RefCell<Option<Rc<Entry>>> — a self-reference
through a strong handle — nothing would ever be dropped and the log would
come back empty. That is the same lesson as the leak problem, at the smallest
possible scale: one node, pointing at itself.
Verified behaviour to expect: after Rc::new_cyclic(|w| SelfRef { me: w.clone() }),
the strong count is 1 and the weak count is 1.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.