We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← The Expert Edge: Idiom, Review and Capstones step 4 of 14
Build Rc<T> and RefCell<T> from scratch
Implement MyRc<T> and MyRefCell<T>, then use the supplied scaffold to observe their reference counts, borrow checks and destruction.
pub fn scaffold(script: Vec<String>) -> Vec<String>
Complete the implementation bodies above the the scaffold banner.
Keep the provided types, command dispatcher, Node, emit and log behavior.
This is a small single-threaded teaching implementation, without Weak
support or the full standard-library APIs.
Implement shared ownership
MyRc owns a heap-allocated RcBox containing a strong count and a value. Implement new, inner, strong_count, try_clone, set_strong, Clone, Deref and Drop.
Start with one counted handle. try_clone uses checked arithmetic to increment the count before returning another handle. Return None if it would overflow, leaving the count unchanged. Clone may use try_clone and panic if it fails. Deref exposes a shared reference to the value.
Drop decrements the count and reclaims the allocation only when the previous count was one. Every pointer access must refer to the original, still-live allocation. Do not add Send or Sync implementations: this reference-counting design is not synchronized.
set_strong is an unsafe test helper that replaces the count. Its caller must keep the number at least as large as the number of live handles. An understated count can release storage while other handles still use it. An inflated count can leak storage. This helper is unsafe because it exposes that obligation to callers, even though its body just writes a Cell.
Implement checked interior mutation
MyRefCell stores its value in UnsafeCell and tracks access with a Cell:
| State | Meaning | New access permitted |
|---|---|---|
| 0 | no retained borrow | shared or exclusive |
| positive n | n shared borrows recorded | another shared borrow if the count fits |
| -1 | exclusive borrow recorded | neither |
try_borrow returns None for an exclusive borrow or shared-count overflow. Otherwise increment the state before returning MyRef. try_borrow_mut succeeds only at zero and changes the state to -1 before returning MyRefMut. Failed attempts must leave the state unchanged.
MyRef implements Deref and decrements the shared count on drop. MyRefMut implements Deref, DerefMut and resets the state on drop. Only the mutable guard provides mutable access to the value. Its references must remain tied to the guard's lifetime.
UnsafeCell permits interior mutation, but does not permit conflicting references. Check and update the state before creating a reference from its raw pointer, and explain why that reference cannot overlap an incompatible live borrow.
Swap through the guards
try_swap acquires an exclusive guard from each cell, then exchanges their values. Return false if either acquisition fails. If the second fails, dropping the first guard must release its temporary exclusive borrow.
This also handles self-swap: acquiring the first guard makes the second
fail. Do not bypass the guards and create two mutable references to one
value. The script reports swap a a busy, leaving the contents unchanged.
Destructors restore availability, but can be skipped
probe checks that a held shared guard blocks mutable access, then that releasing the guard allows mutable access again. Repeating the command should give the same results.
This checks normal cleanup, not a guarantee that all destructors run. A caller can use mem::forget on a guard. The recorded borrow then remains, and later access may stay blocked. That loss of availability is acceptable; creating conflicting references is not. Checked shared-count increments also prevent forgotten borrows from eventually wrapping the state.
Likewise, forgetting a MyRc handle retains a count and may leak the value. Unsafe abstractions must tolerate safe callers skipping destruction.
Strong cycles retain values
Nodes can retain MyRc handles to other nodes. If a and b retain each other, removing the external handles leaves each with one strong owner. Their destructors do not run, so the cycle example has no drop lines. This implementation does not collect cycles or provide Weak handles.
The composition MyRc<MyRefCell<Node>> does not inherently leak. A retained strong cycle or an intentionally inflated count creates the leak here. In a fuller design, non-owning back-references can avoid such cycles.
Read the supplied command behavior
Commands use split_whitespace, so names, payloads and write text are
single tokens. Commands require exactly the listed arguments; other
shapes append bad <original line>.
| Command | Effect |
|---|---|
| new name payload | replace that world's entry with one new node |
| clone name | try to add another external handle |
| release name | release the last external handle stored under that name |
| count name | log its current strong count |
| read name / write name text | access the payload through a guard |
| probe name | log shared access, conflicting access and access after release |
| swap a b | exchange entire Node values, including their links |
| link a b | replace a's link with a cloned handle to b |
| poison name | set the strong count to usize::MAX for overflow testing |
Missing entries are logged by the dispatcher. Replacing an entry or link may run a destructor before the command's own log message. poison deliberately permits a leak; a later clone command reports refused. A link to a poisoned target instead uses Clone and can panic, so do not assume every script completes. The supplied poison fixture targets a 64-bit runtime.
After all commands, the scaffold logs teardown and releases world entries in sorted key order. Links can keep nodes alive longer or cause nested destruction, so actual drop lines follow ownership, not just name order.
What verification establishes
Output tests exercise counts, self-swap, borrow conflicts, overflow and cycles. They do not prove that every unsafe operation is sound. Incorrect unsafe code can appear to return the expected log.
Include a SAFETY comment for each unsafe block and a Safety section for unsafe functions. Explain pointer validity, ownership and aliasing rather than treating a passing test as justification. The submission must compile, match the logs and pass Clippy with warnings denied.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.