Skip to content

← The Expert Edge: Idiom, Review and Capstones step 5 of 14

Hard End-to-End

Capstone: an ownership-only job scheduler

Repair a small scheduler while preserving its command behavior and log order.

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

Jobs move between a pending queue and one running slot. Each job owns an ID and payload, and its Drop implementation appends drop <id> to a shared log. Keep Job non-Clone and preserve that destructor.

Commands and exact log order

Split each input line with split_whitespace. Commands are case-sensitive and require exactly the listed tokens. IDs and payloads are single tokens; there is no quoting syntax. Extra or missing tokens make a command invalid.

Command Behavior and log
submit <id> <payload> Append a new job to pending; log submit <id>.
start <id> Remove the first matching pending job. If another job is running, log preempt <oldid> and move it to the front of pending. Then log start <id> and install the selected job as running.
finish Consume the running job, logging done <id>:<UPPERCASE-PAYLOAD>, followed immediately by drop <id>. If idle, log finish idle.
cancel <id> Remove the first matching pending job, logging cancel <id> before its drop <id>.
snapshot Log pending IDs in queue order as pending a,b,c, or pending - if empty. Leave the queue unchanged.

A missing pending job produces start <id> missing or cancel <id> missing without changing state. In particular, a failed start does not preempt the running job, and cancel does not inspect the running slot.

Duplicate IDs are allowed. Searches choose the first matching pending job, even if another job with that ID is running. Uppercasing uses Rust's String::to_uppercase, including Unicode behavior.

Invalid commands log bad <line>, retaining the original line's whitespace. At the end, log shutdown, drop the running job if present, then drop pending jobs from front to back. An empty script returns ["shutdown"].

For example:

Commands:
submit a alpha
submit b beta
start a
start b
finish

Log:
submit a
submit b
start a
preempt a
start b
done b:BETA
drop b
shutdown
drop a

Start: read before moving

The starter assigns Some(job) to running, then tries to read job.id. Moving the job makes that local binding unavailable, causing E0382. Log its ID before the move, or access it through its new owner.

Moving a job does not invoke its destructor. That is why preemption returns a job to the queue without a drop line.

Finish: preserve fields needed by Drop

Destructuring job to move out its strings causes E0509 because Job implements Drop. Its destructor must receive a valid whole value.

Give Job a completion method that takes self. It can borrow the ID and payload to construct the completion line, emit it, and let the job be dropped when the method ends.

Implementing Drop does not forbid every field operation. With mutable access, std::mem::take or replace can move a field out while leaving a valid replacement. That is unnecessary for completing this job.

The supplied redundant-clone lint catches some unnecessary clones; it does not enforce a universal ban on cloning. Keep the ownership-based design requested here instead of adding Clone to Job. The supplied Rc::clone shares the log handle, not the job.

Cancel: keep an index, not a borrowed job

The starter finds a reference inside the queue, removes an element, and then uses that reference. The overlapping immutable and mutable borrows cause E0502.

Use position to obtain an index. Once the search finishes, the index does not keep the queue borrowed. Emit the cancellation line before removing and dropping the job.

Snapshot: borrow IDs

Iteration yields &Job. Moving j.id out of that shared reference causes E0507. Use j.id.as_str() to borrow each ID for joining.

This avoids cloning each ID, but constructing the reference vector and joined log string can still allocate. A borrowed ID does not make the whole snapshot operation allocation-free.

Check lifecycle behavior

The log exposes destructor calls, not every ownership move. Expected traces help detect early drops, missing completion messages, and shutdown ordering. They are evidence for the tested paths, not a general proof that values cannot leak or that destructors always run under every exit condition.

Keep the explicit shutdown loop so front-to-back destruction follows from the code. Do not rely on an assumed container destructor order.

A passing submission must compile, pass the tests, and pass clippy -D warnings.