Skip to content

← Ownership I: Moves, Copy, Clone, Drop step 19 of 20

Hard Primitives

Observe when destructors are skipped

pub fn leak_report(script: Vec<String>) -> Vec<String>

Implement a command interpreter that records when values are created and destroyed. Compare ordinary destruction with mem::forget, ManuallyDrop and an Rc reference cycle. Use the supplied logging helpers rather than constructing the expected log directly.

Commands and output

Process commands in order and return the final log. Match the prefixes below exactly, including their trailing space. Names are the entire remaining text: they may be empty or contain spaces, and must not be trimmed. Matching is case-sensitive. Only the exact string cycle invokes the cycle helper.

Command Action Log entries
normal N Create a tracked value, then drop it before the next command. new N, drop N
forget N Create a tracked value and pass it to std::mem::forget. new N
manual N Create a tracked value and wrap it in std::mem::ManuallyDrop::new. new N
cycle Call make_cycle(log.clone()). new cyc0, new cyc1
Anything else Record the complete command after ? . ? <command>

An empty script returns an empty log. For example:

Input:  ["normal a", "forget b", "manual c", "normal d"]
Output: ["new a", "drop a", "new b", "new c", "new d", "drop d"]

Two of these four tracked values are destroyed: a and d. The destructors for b and c are skipped. Bare normal is an unknown command, while normal creates and drops a value with an empty name.

Supplied helpers

new_log() creates a shared log. tracked(name, log) returns a Tracked value and records new <name>. Its Drop implementation records drop <name>. Use note(log, line) to append an unknown-command entry and snapshot(log) to return a copy of the log. Pass log.clone() to helpers when you need to retain your own handle; this shares the log rather than copying its entries.

make_cycle(log) creates two Rc nodes containing tracked values named cyc0 and cyc1. Each node holds a strong reference to the other. When the helper returns, dropping its local handles does not reduce either strong count to zero. The cycle remains allocated and neither tracked destructor runs. A cycle of strong references causes this behavior; an arbitrary use of Rc, or a cycle broken with Weak, does not necessarily leak.

Destruction is useful, but not unconditional

Rust normally drops owned values when their scope ends or when they are explicitly passed to drop. Ownership can also move elsewhere, delaying destruction. Some safe operations deliberately prevent it:

  • mem::forget(value) consumes the value without running its destructor.
  • ManuallyDrop::new(value) suppresses automatic destruction of its contents. It does not itself allocate. This exercise leaves the contents undropped; other code can safely recover them with ManuallyDrop::into_inner.
  • A strong Rc cycle can keep values alive after external handles are gone.

Leaks can waste memory or leave resources unreleased, so they are usually undesirable. They are not, by themselves, undefined behavior. This distinction explains why mem::forget is safe. It does not promise that arbitrary raw pointers remain valid after a value is forgotten.

An unsafe abstraction must remain memory-safe when a caller forgets a value it returns. For example, a drain iterator cannot rely solely on its destructor to repair a vector containing moved-out elements. The later unsafe-code exercises examine that design; this exercise focuses on observable drop logs.

Repair the starter

The starter tries to move t.name out of a Tracked value that implements Drop. Rust rejects that partial move with E0509. Pass the entire t to mem::forget, and do not use it afterward. The extra forgot ... entry in the starter is also outside the required output contract.

Keep the manual value inside its wrapper without manually dropping or extracting it. The absence of a drop entry is intentional here. Clippy accepting the reference does not mean leaking resources is generally good practice, and log comparisons alone cannot prove an implementation uses the requested ownership operations.