Skip to content

← Smart Pointers and Interior Mutability step 18 of 26

Hard Primitives

Rc::new_cyclic: self-reference in one step

Build entries that retain a Weak handle to themselves, then observe construction, ownership counts and cleanup.

pub fn registry(names: Vec<String>) -> Vec<(String, bool, usize)>
pub fn cyclic_facts(names: Vec<String>) -> (Vec<bool>, Vec<String>)

Use Rc::new_cyclic in the supplied make helper. Each Entry stores its name, a self-reference me: Weak<Entry>, a seen_in_closure flag and a shared destructor log. Keep this structure and its Drop implementation.

Create a separate entry for every input position, including repeated names. Preserve empty strings and Unicode names without modification.

What each function returns

registry returns one (name, alive, strong_count) tuple per entry in input order. alive reports whether the stored Weak can be upgraded after construction. Measure the strong count with only the registry's handle retained for that entry, so every row has alive = true and count = 1.

cyclic_facts returns two vectors:

  • The recorded seen_in_closure flags in input order. Each records whether the Weak supplied to the construction closure could be upgraded there.
  • The actual destructor log after releasing all entries, also in input order. Explicitly drop entries in that order before reading the log.

For ["a", "b"], registry returns [("a", true, 1), ("b", true, 1)], and cyclic_facts returns ([false, false], ["a", "b"]). Empty input gives empty vectors.

Construct the value with its weak self-reference

Rc::new_cyclic prepares the allocation, calls your closure with a &Weak<Entry>, then initializes the value using the Entry returned by that closure. Clone the supplied Weak into the me field.

During the closure, upgrade returns None: construction is not complete. Record that result rather than trying to unwrap it. After new_cyclic returns, the stored Weak can be upgraded while the entry has a strong owner.

This lets the self-reference be initialized together with the other fields. It avoids constructing a placeholder and then mutating the field through a shared Rc. The starter tries that direct assignment and gets E0594.

A plain Weak field does not enforce a correct self-reference by itself. Weak::new() is still a valid value of that type. The make helper must deliberately store a clone of the Weak supplied by new_cyclic.

Count before retaining an upgraded handle

A successful upgrade creates another strong Rc handle. Its lifetime matters when you inspect strong_count.

In the starter's tuple expression, the successful upgrade is a temporary that remains alive while the later tuple element reads the count. That reports two instead of one. Read the count in a separate statement before upgrading, as the exercise requires. Alternatively, release an upgraded handle before measuring; do not leave it in a local variable unintentionally.

The stored Weak does not contribute to the strong count. Immediately after this helper returns, the entry has one strong owner and one explicit Weak handle stored inside it, before any additional handles are created.

Release entries in a defined order

Weak self-references do not keep Entry values alive. Once an entry's last strong owner is released, its destructor appends the name to the log. Keep the log's own Rc alive until you have read it.

Vec does not guarantee the order in which its destructor drops elements. Do not rely on drop(entries) for the required log order. Consume the vector in a loop and call drop on each entry in sequence, then read the log.

A retained strong self-reference would instead keep the count above zero until that cycle was broken. new_cyclic provides a Weak during construction; it does not prevent later code from introducing strong ownership cycles.

Scope of the technique

This problem uses a direct weak self-reference. More complex structures can also use new_cyclic, including constructing a child that retains the supplied Weak as a back-reference to its parent. It is not a general prohibition on mutual links, nor does it initialize arbitrary graphs automatically. Plan construction order and ownership for each link.

Complete both functions and the helper. The submission must compile, match the observations and destructor order, and pass Clippy with warnings denied.