Skip to content

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

Easy Primitives

drop(x) is just a function

Write drop yourself, then use it to schedule destruction.

fn my_drop<T>(_value: T) {}

pub fn schedule(names: Vec<String>, drop_after: Vec<usize>) -> Vec<String>

The least magical function in the standard library

Learners treat drop as compiler magic. Here is its actual definition, from core::mem:

pub fn drop<T>(_x: T) {}

That is all of it. An empty body. There is no intrinsic, no compiler hook, no special case in the type checker. It works for exactly one reason: _x is taken by value, so calling drop(t) moves t into the function, and when the function returns, its local parameter goes out of scope and Rust destroys it like any other local.

Sit with that for a moment, because it retroactively explains every by-value signature you have seen in this track. fn tag(l: String) -> String does not merely “take a String” — it takes responsibility for destroying it unless it passes that responsibility on. Ownership is not a rule layered on top of the language; it is the mechanism, and drop is the smallest possible demonstration.

::: question Why is fn my_drop<T: Drop>(_value: T) {} wrong? Because Drop is not “types that get destroyed” — it is “types with a hand-written destructor”. String does not implement Drop. Neither does i32, nor Vec<T>, nor almost anything you write. They are all still dropped; they simply have no custom drop method, so their destruction is the compiler recursively destroying their fields.

Adding T: Drop would make my_drop(String::new()) a compile error:

error[E0277]: the trait bound `String: Drop` is not satisfied

which is the opposite of what you want. std::mem::drop has no bound at all, and that is deliberate — it must accept everything, because everything gets dropped.

(There is a related joke in the standard library: drop on a Copy type does nothing whatsoever, since the original is still there. rustc’s dropping_copy_types lint warns you about it, and clippy’s drop_non_drop warns when you explicitly drop a value with no destructor anywhere in it — usually a sign you meant something else.) :::

The other half: x.drop()

Drop::drop is a real method, so you would expect t.drop() to work. It is specifically forbidden:

error[E0040]: explicit use of destructor method
  |
  |     t.drop();
  |       ^^^^ explicit destructor calls not allowed
  |
help: consider using `drop` function
  |
  |     drop(t);

The starter contains that line, so you will meet the error rather than take my word for it. The reason for the ban is a double-free: Drop::drop takes &mut self, so calling it directly would run the destructor without consuming the value, and the value would then be destroyed again at the end of its scope. drop(t) cannot have that problem — it takes t by value, so t is gone afterwards and the compiler knows it.

That is also why you can implement Drop::drop and never call it. You write the destructor; the compiler decides when it runs.

What to build

schedule(names, drop_after)

Walk names in order, building a Tracked for each (the grader’s scaffolding logs "new <name>" on construction and "drop <name>" in its destructor).

  • If the value’s index appears in drop_after, hand it straight to my_drop — it dies immediately, and the log shows that.
  • Otherwise keep it.

When the loop is done, release the survivors newest first, one at a time, also through my_drop. Then return the log.

For names = ["a", "b", "c"] and drop_after = [1]:

new a, new b, drop b, new c, drop c, drop a

Indices in drop_after that are out of range select nothing. The scaffolding:

struct Tracked { name: String, log: Log }   // NOT Clone
fn tracked-like constructor: Tracked::new(name: String, log: Log) -> Tracked
fn new_log() -> Log
fn snapshot(log: Log) -> Vec<String>

Tracked is deliberately not Clone, so there is no way to duplicate your way around a move here.

A hint about the survivors

Vec::pop removes from the back, which is exactly “newest first”. Popping in a while let Some(t) = kept.pop() loop and passing each t to my_drop gives the required order and — worth noticing — is the same shape as the relay problem near the start of the track.

Remember the grade is compile + tests + clippy -D warnings.

Loading visualization…