We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 27 of 28
Read a log after its closure finishes
Process a list while recording one log entry for each input value. Return the output values and the completed log:
pub fn instrument(nums: Vec<i64>) -> (Vec<i64>, Vec<String>)
Visit values in input order:
- For a negative
n, append"skip:{n}"to the log and emit no value. - Otherwise, append
"keep:{n}"and emitn * 3. Zero is kept.
After processing the entire input, append "events:K", where K counts
the preceding log entries. It therefore equals the input length, including
skipped values. This summary does not count itself. Preserve duplicates and
use the exact strings shown, without spaces or newlines.
[1, -2, 3] -> ([3, 9], ["keep:1", "skip:-2", "keep:3", "events:3"])
[] -> ([], ["events:0"])
[0] -> ([0], ["keep:0", "events:1"])
Nonnegative inputs are at most i64::MAX / 3, so tripling them fits in i64.
Negative values can include i64::MIN; they are logged and skipped before
multiplication. The output and log fit in memory.
Use an FnMut closure that mutably borrows the log vector for the per-value
entries. Finish using that closure before reading the log length, then push
the summary directly onto the vector. This exercise practices sequencing
ordinary borrows; keep the log as a Vec<String> without interior mutability.
Why the starter fails
Creating record borrows log mutably because its body calls log.push.
The starter reads log.len() and then calls record again to write the
summary. That later call keeps the mutable borrow active across the read,
so the compiler reports E0502.
The conflict is between direct access to log and the borrow held by
record. It does not prevent the closure itself from accessing the vector
through that borrow. Nor does every FnMut closure borrow external state:
a move closure can own its captured state instead.
End the borrow before reading
Replace the final record(...) call with log.push(...). The closure's last
use is then in the pipeline consumed by collect(). After collection, the
borrow checker can end that borrow and allow direct access to log.
You may also place the closure and pipeline in an inner block, as long as that block returns the collected output. This makes the separation visible, but an extra block is not required for this closure.
Here is a complete example of a captured borrow ending after the last call:
fn main() {
let mut count = 0;
let mut increment = || count += 1;
increment();
assert_eq!(count, 1);
}
Moving the assertion before increment() would put a shared read inside
the mutable borrow's lifetime and fail to compile. The relevant boundary
here is the closure's last use, not necessarily its enclosing closing brace.
Consume the pipeline before inspecting its effects
Iterator adapters such as filter are lazy. Constructing a pipeline alone
does not visit the inputs or write their log entries. collect() drives
this pipeline to completion, so the final length is read after all entries
have been written.
Other designs may need shared access to mutable state. RefCell can support
that within one thread by checking borrows at runtime; conflicting borrows
panic unless handled through its fallible borrowing methods. It is an option,
not an automatic requirement whenever two closures use the same state.
Here, completing the closure's work first is enough.
Your solution must compile, follow the required closure structure, match the output and log, and pass Clippy with warnings treated as errors.
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.