We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership I: Moves, Copy, Clone, Drop step 6 of 20
Move values through a pipeline without cloning
Process names through a two-stage pipeline and return its event log. Each stage takes ownership of a value and returns it for the next stage.
pub fn pipeline(names: Vec<String>) -> Vec<String>
For each name, in input order, create a Tracked value, measure it, then mark
it with the measured count as a decimal string. Keep every processed value
alive until all names have been processed. Finally, drop the values in input
order and return a snapshot of the log. Do not clone any Tracked value.
Empty input returns an empty log. Empty names, repeated names and Unicode
names are valid. Preserve each name exactly. The measured count is its number
of Unicode scalar values, as returned by chars().count(), not its byte count
or number of displayed grapheme clusters.
Helpers supplied by the grader
These helpers are available without imports. Log is an
Rc<RefCell<Vec<String>>>, and Tracked owns a name and a handle to that log.
| Call | Result and effect |
|---|---|
new_log() |
Returns an empty Log. |
Tracked::new(name, log) |
Takes a String and a Log, records new <name>, and returns a Tracked. |
measure(t) |
Takes a Tracked, records measure <name>, and returns (t, count) with a usize count. |
mark(t, note) |
Takes a Tracked and a String, records mark <name> <note>, and returns the value. |
drop(t) |
Consumes the value and runs its destructor, recording drop <name>. |
snapshot(log) |
Takes a Log handle and returns a cloned Vec<String> of its current entries. |
Tracked also has a custom Clone implementation. It records clone <name>,
clones the name string and shares the same log. Cloning a Log handle itself
does not produce a clone event: that operation clones an Rc, not a
Tracked. Use a cloned log handle when constructing each value.
For names ["ada", "bo"], return these eight strings:
new ada
measure ada
mark ada 3
new bo
measure bo
mark bo 2
drop ada
drop bo
The spaces in each entry are literal. An empty name produces new ,
measure , mark 0 and drop , including the trailing or doubled spaces.
Keep the value returned by each stage
The starter passes t to measure, discards the returned value, and then
tries to use the old binding. That produces E0382: use of a moved value.
Capture both parts of the result with let (t, n) = measure(t); and pass
the returned t to mark.
This pattern also works without the logging helpers:
fn measure_name(name: String) -> (String, usize) {
let count = name.chars().count();
(name, count)
}
fn main() {
let name = String::from("héllo");
let (name, count) = measure_name(name);
assert_eq!(name, "héllo");
assert_eq!(count, 5);
}
Moving the value through the calls avoids duplicating its name buffer. The exercise as a whole still allocates for log entries, notes, the vector of retained values and the final snapshot.
Make destruction order explicit
A Vec owns its elements, but its automatic element-drop order is not a
documented guarantee. Since this task requires an exact event order, consume
the retained values in input order and call drop on each one before taking
the snapshot. for t in alive { drop(t); } does that explicitly.
Taking the snapshot too early omits the drop events. Dropping each value immediately after marking it interleaves the drop events with later names. Both produce a different log from the requested pipeline.
What cloning means depends on the type
Clone is implemented by each type. It does not promise a deep copy, a fixed
allocation count or a particular runtime cost.
| Type | What its clone does |
|---|---|
i64, bool, char |
Copies a scalar value. Assignment already copies these types. |
Shared reference &T |
Copies the reference, retaining access to the same value. |
String |
Duplicates the string contents into independently owned storage; copying a nonempty string requires work proportional to its bytes. |
Vec<T> |
Clones its elements; their Clone implementations determine whether they copy data, share it or do other work. |
Rc<T> / Arc<T> |
Creates another owning handle to the same allocation, updating the reference count without cloning T. |
HashMap<K, V> |
Clones keys and values, along with its hasher; cost depends on their implementations and the map's storage. |
Empty buffers and zero-sized elements make fixed allocation counts misleading.
A Vec<String> duplicates its strings, while a Vec<Rc<String>> clones handles
to shared strings. Cloning every separate name once costs work proportional
to their total size; cloning the entire collection on every iteration can
multiply that work by the number of iterations.
In this task, cloning the log handle is intentional sharing. Cloning a
Tracked introduces an extra event and an extra value to destroy, so it
cannot replace using the value that measure already returns.
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.