We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership I: Moves, Copy, Clone, Drop step 15 of 20
Conditional moves and drop flags
Create tracked values, drop some immediately, and keep others until a final cleanup phase. Return the complete creation and destruction log.
pub fn conditional_drops(flags: Vec<bool>) -> Vec<String>
Each flag controls one value. Process flags in input order, including
repeated values. The index starts at zero and becomes part of the name:
t0, t1, and so on.
Helpers supplied by the harness
| Helper | Behavior |
|---|---|
new_log() |
Creates a shared, initially empty Log. |
tracked(name, log) |
Appends new <name> and returns a Tracked value whose name field your code can read. |
Tracked's destructor |
Appends drop <name>. |
note(log, line) |
Appends the supplied line unchanged. |
snapshot(log) |
Returns a copy of the log entries. |
Cloning a Log shares the same log; it does not clone a tracked value.
The starter already provides the conditional_drops driver. Keep it and
implement run(flags, log). The driver reads the log after run returns,
so destruction performed inside run is included in the result.
Required sequence
1. Process the flags. For each index i, create t{i}. If its flag is
true, move the value into a vector of survivors. Otherwise, drop it before
processing the next flag.
2. Prepare the banner. Count the survivors. If there are none, append
no banner and return. This also applies to empty input. Otherwise, create
a value named banner{count}, then append ready banner{count}.
3. Clean up in a specified order. Drop the banner first. Then consume the
survivors in input order, dropping each one before advancing to the next.
Use an explicit loop to establish this order: Vec does not guarantee its
element destruction order when the vector itself is dropped.
For flags = [true, false, true], return these separate strings:
new t0
new t1
drop t1
new t2
new banner2
ready banner2
drop banner2
drop t0
drop t2
The exercise is about ownership and cleanup, so use the supplied tracked values to produce the log. The harness checks the returned entries rather than inspecting your implementation.
Why the starter fails
In phase 1, the starter calls drop(t) when the flag is false, then tries
to push t into the survivors on every path. drop takes ownership, so
the false path has already moved the value. This produces E0382, a use of a
moved value. Put the two outcomes in separate branches: either keep t
or drop it.
The banner has a different problem. The starter declares banner without
initializing it, assigns it only when survivors exist, then reads its name
on both paths. E0381 reports that the binding may be uninitialized. Return
from the empty-survivor case before declaring and using the banner.
These errors concern whether a value is definitely available at a use. Cleanup must also handle paths on which a value has already moved or was never initialized, but a runtime drop flag does not make an invalid use legal.
What a drop flag represents
The compiler may need runtime state to decide whether a value still needs destruction. A drop flag records that initialization state. Where the state is known statically, cleanup can be unconditional or omitted. Exact storage, branches and optimization depend on the generated program; there is no fixed one-byte cost or guarantee that optimization removes all flags.
The log here observes destructor behavior, not the compiler's chosen representation. Drop flags are not named Rust variables your program can read. Compiler intermediate output or generated code can help investigate their implementation, but those details are not a stable program interface.
A conditional move does not permanently invalidate a binding. You can initialize it again before a later use, or make the path that moved it leave the function. This complete example shows both approaches:
fn replace_if_needed(replace: bool) -> String {
let mut value = String::from("original");
if replace {
drop(value);
value = String::from("replacement");
}
value
}
fn keep_unless(discard: bool) -> Option<String> {
let value = String::from("kept");
if discard {
drop(value);
return None;
}
Some(value)
}
fn main() {
assert_eq!(replace_if_needed(false), "original");
assert_eq!(replace_if_needed(true), "replacement");
assert_eq!(keep_unless(false).as_deref(), Some("kept"));
assert_eq!(keep_unless(true), None);
}
Keep initialization close to use
Deferred initialization is legal when every path reaching a use supplies a
value. It can be helpful when control flow needs it. Clippy's
needless_late_init identifies some cases that can instead use a declaration
at the assignment or an expression-valued if.
Here, a guard clause makes the banner's lifetime clear: return when there are no survivors, then construct it normally. After the ready marker, explicitly drop the banner and then each survivor. Submissions must compile, produce the expected logs and pass the runner's Clippy checks.
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.