We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 3 of 28
move: when the closure must own its captures
Build one validator closure per limit, then run a fixed set of probes past all of them.
pub fn make_validators(limits: Vec<i64>) -> Vec<i64>
For each limit, the validator answers x <= limit. The probe set is fixed
and given to you:
const PROBES: [i64; 6] = [-10, 0, 1, 5, 10, 100];
Return, for each probe in that order, how many validators accept it. With
limits = [10, 5, 100] the answer is [3, 3, 3, 3, 2, 1]: every validator
accepts -10, 0, 1 and 5; only the 10 and 100 validators accept
10; only the 100 validator accepts 100. An empty limits gives six
zeroes.
The starter is one word away from correct
Compile it. You get exactly one error, and it is the error that sends more beginners to Stack Overflow than any other closure diagnostic:
error[E0373]: closure may outlive the current function, but it borrows
`limit`, which is owned by the current function
Read it literally, because it is telling the exact truth. limit is a local
— it is the parameter of the map closure, and it dies when that closure
returns. The inner closure only borrows it. But you are putting that inner
closure into a Box<dyn Fn(i64) -> bool>, and a bare dyn Trait in a Box
means dyn Trait + 'static: the compiler has been told this value may live
forever. A thing that lives forever cannot hold a reference to a thing that
dies at the end of the line. Rejected.
The fix is move.
What move actually does
move changes one thing: it forces every capture to be taken by
value instead of by reference. That is the entire semantics. It is a
property of how the closure gets its data, not of the closure’s type, not
of when it runs, not of how many times it can be called.
Without move, the compiler infers the least restrictive capture mode that
makes the body compile — usually a shared borrow. With move, Copy types
are copied in and non-Copy types are moved in, and the closure owns
whatever it holds from then on.
This is why thread::spawn(move || ...) is written the way it is. Without
the move you get E0373 about the captured variable; the closure would
borrow from a stack frame that may vanish while the thread is still running.
move is not thread magic. It is “give the closure its own copy”, and
threads are just the most common place you need it.
move does not make a closure FnOnce
Say it out loud, because half the internet gets this wrong. From item 9.2:
the trait a closure implements is decided by what its body does to the
captures, never by move.
let n = 5i64;
let f = move |x: i64| x + n;
println!("{} {} {}", f(1), f(2), f(3)); // fine — `f` is `Fn`
Here the validators are all move and all Fn, which is why they can sit in
a Vec and be called once per probe — six times each.
A move closure can itself be Copy
This is small, exact, and frequently useful. A closure’s captured state is its fields, so the closure struct derives its own auto traits from them:
let n = 5i64;
let f = move |x: i64| x + n;
let g = f;
println!("{} {}", g(1), f(2)); // fine — `i64: Copy`, so `f: Copy`
let s = String::from("hi");
let h = move |x: i64| format!("{s}{x}");
let k = h;
// h(2); // E0382: use of moved value — `String` is not `Copy`
Same move, opposite outcome, and the only difference is what got captured.
The same reasoning decides whether your closure is Send and Sync: it is
if all its captures are. There is no closure-specific rule to learn — it is
the ordinary struct rule applied to a struct you did not write.
The loop-variable trap this problem is really about
The bug in the starter has a nastier cousin. If you build closures in a
for loop without move, each one borrows the loop variable, and the borrow
ends when the iteration ends — so the error appears at the closing brace of
the loop, or worse, at the return, several lines away from the closure you
actually wrote wrong. When a closure error points somewhere that makes no
sense, ask first “which local did this borrow, and when does that local die?”
Grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.