We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership I: Moves, Copy, Clone, Drop step 10 of 20
Partial moves: taking one field and leaving the rest
Take structs apart field by field.
pub fn repack(records: Vec<(i64, String, Vec<String>)>) -> Vec<String>
Ownership is finer-grained than you think
So far “moved” has been a property of a variable. It isn’t. It is a property of a place — a variable, a field, a field of a field. The compiler tracks each independently, which is why this is legal:
let r = Record { id: 7, label: String::from("hi"), tags: vec![] };
let label = r.label; // `r.label` has moved; `r.id` and `r.tags` have not
let id = r.id; // fine — and would be fine even if `label` were a Vec
and why the error for over-reaching says something slightly different from the E0382s you have seen so far:
error[E0382]: use of partially moved value: `r`
|
| let label = r.label;
| ------- value partially moved here
| let r = Record { label: String::from("anon"), ..r };
| ^ value used here after partial move
“Partially moved” is a real state. r is neither alive nor dead: some of it is
still there, and the compiler knows exactly which parts. You may keep reading
the fields that have not moved. You may not use r as a whole, because a whole
Record is no longer what is sitting in that stack slot.
This is the same insight that later explains disjoint closure capture (a closure
that touches r.label does not capture r.tags) and split borrows. It is also
the most common real shape in ordinary code: “I just want the name out of this
struct.”
The type
The grader injects this. Note what is not on it — no Clone, no Drop:
struct Record {
id: i64,
label: String,
tags: Vec<String>,
}
The absence of Clone means there is no escape hatch: you cannot duplicate your
way out of a partial move here. The absence of Drop is what makes the whole
exercise possible at all, and the next problem is about what happens when a
destructor shows up.
What to build
For each input tuple (id, label, tags):
-
Build a
Recordfrom the three pieces. -
If the label is empty, replace it — using struct update syntax —
with
"anon", keeping the other fields:Record { label: String::from("anon"), ..r }. -
Move
labelout of the record. Movetagsout. Readidafter both of those moves, to prove theCopyfield survived. -
Push
format!("{id}:{label}[{}]", tags.join("+")).
(1, "a", ["x", "y"]) gives "1:a[x+y]". (7, "", ["a"]) gives "7:anon[a]".
::: question Step 2 has to happen before step 3, and the compiler is quite firm about it. Why?
..r is not a borrow and not a copy — it moves every remaining field out of
r and into the new struct. That requires r to be whole. If label has
already left, there is nothing coherent for ..r to take, and rustc rejects it
as a use of a partially moved value.
Struct update syntax is easy to misread as “copy the rest”, especially if you
have written JavaScript spread or Python **kwargs. It is not. For Copy
fields it does copy; for everything else it moves, and the source struct is
partially moved afterwards exactly as if you had written the field assignments
out by hand.
There is a corollary worth banking: Record { ..r } with no overridden fields
at all is just r written the long way, and clippy has a lint for it
(unnecessary_struct_initialization, allow-by-default).
:::
The lint you will meet
The starter builds the record like this:
let r = Record {
id: id,
label: label,
tags: tags,
};
Once you have fixed the move errors, clippy will refuse the submission:
error: redundant field names in struct initialization
help: replace it with: `id`
redundant_field_names is on by default, and it is the right call — when
the field and the local share a name, Record { id, label, tags } says the same
thing with less to read. This is a small taste of a theme that runs through the
whole course: the gate is not only about correctness.
Watch out
The type must not gain a Drop impl. Give a struct a destructor and every
one of these partial moves becomes E0509 instead, because a destructor is
entitled to receive all of its fields intact. That is the next problem, and it
is the hardest one in the beginner half of this track.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.