Skip to content

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

Medium Primitives

Observe scope cleanup and control vector drop order

Build the eight arrangements below and return the log produced by their destructors and marker calls. Use the supplied Tracked and Trio helpers; do not hard-code the returned event lists.

pub fn order_report(scenario: i64) -> Vec<String>

Scenarios 0 through 7 select one arrangement. Every other i64 input returns an empty log. Scene 0 is already implemented in the starter.

Helpers supplied by the harness

Construction is silent. A Tracked destructor appends "drop " followed by its name. Trio's destructor appends "drop trio" before its fields drop.

Helper Purpose
new_log() -> Log Creates the shared event log.
tracked(name: String, log: Log) -> Tracked Creates a named value.
note(log: Log, line: String) Appends an exact marker string.
snapshot(log: Log) -> Vec<String> Copies the current log.
trio(a: String, b: String, c: String, log: Log) -> Trio Creates first, second and third Tracked fields with those names.

Clone Log handles when several values need access to the same log. Trio's fields are declared first, second, third, then log. Neither cloning a handle nor dropping the handle adds an event.

Build these scenarios

Create locals in the order stated. Except for the explicit vector cleanup in scene 2, let values drop when their scope ends. Return the snapshot only after the scene has finished.

Scene Arrangement
0 Three locals holding Tracked values named a, b and c.
1 One Trio with field names p, q and r.
2 A tuple of x and y; then an array of p and q; then a Vec of u and v. At the end, consume the Vec in forward order and explicitly drop each element. Let the array and tuple drop automatically.
3 Local a; an inner block containing local b and marker "inside"; after that block, marker "after"; then local c.
4 Marker "start"; let _ = tracked(...) creating imm; marker "mid"; let _held = tracked(...) creating held; marker "end".
5 Two successive locals both named _s, holding v and then w; marker "done".
6 Local a; an inner block with local b and marker "inner"; return from the scene function immediately.
7 As scene 6, but instead of returning, emit "stayed", leave the inner block, then emit "after".

In scene 2, use Vec::from([ ... ]) for the vector and a consuming loop such as for value in queue { drop(value); }. This enforces u before v. The full scene 2 log is ["drop u", "drop v", "drop p", "drop q", "drop x", "drop y"]. An unused vec! can attract Clippy's useless_vec suggestion; the explicit Vec construction also makes the exercise's intended container clear.

Which orders are guaranteed?

For the initialized, unmoved values in these scenes:

Arrangement Cleanup order
Locals in one scope Reverse declaration order.
Struct and tuple fields Declaration order.
Array elements First to last.
A type with custom Drop Its destructor body, then its fields.
Nested scopes on normal return Inner scopes before enclosing scopes.

A Vec does not guarantee its automatic element-drop order. That is why scene 2 explicitly consumes it. Observing one compiler drop its elements forward is not proof of a portable rule. See the Vec guarantees.

Dropping a borrowed slice reference does not destroy the referenced elements. Also, field destruction order is not a rule for evaluating a struct's field initializers: those expressions are evaluated in their written order.

A wildcard does not always drop a value

In scene 4, tracked creates a temporary. The wildcard does not bind it, and this temporary drops at the end of the statement. By contrast, using a wildcard with an existing local does not move or immediately destroy it. This complete example shows the difference:

struct Notice(&'static str);

impl Drop for Notice {
    fn drop(&mut self) { println!("drop {}", self.0); }
}

fn main() {
    let value = Notice("local");
    let _ = value;
    println!("still here: {}", value.0);
    let _ = Notice("temporary");
    println!("after temporary");
}

It prints, in order: still here: local, drop temporary, after temporary, drop local.

_held is an ordinary binding despite its leading underscore. Shadowing _s creates another binding; it does not replace and drop the earlier value. In scene 5 both survive to the scope's end, so w drops before v.

Keep normal return separate from panic behavior

Scene 6 uses an ordinary return, which runs scope cleanup; it is not panic unwinding. An unwinding panic also cleans up scopes, but aborting execution does not. This exercise does not ask you to trigger a panic.

These are the rules needed for the scenes, not a complete account of all temporary lifetimes. Temporary scopes can depend on expression context and edition. Closure capture destruction order is unspecified. Consult the destructor reference when a different arrangement depends on those details.

Passing requires the exact logs, compilation and Clippy with warnings treated as errors.