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 count how many validators accept each of six fixed probes.
pub fn make_validators(limits: Vec<i64>) -> Vec<i64>
A validator accepts x when x <= limit. Test these probes in this order:
const PROBES: [i64; 6] = [-10, 0, 1, 5, 10, 100];
Return six counts, one per probe. Each input entry contributes a validator,
including duplicate limits. Empty input produces [0, 0, 0, 0, 0, 0].
Limits may be any i64 value; the comparison needs no arithmetic.
For limits = [10, 5, 100]:
| Probe | Limits that accept it | Count |
|---|---|---|
| -10 | 10, 5, 100 | 3 |
| 0 | 10, 5, 100 | 3 |
| 1 | 10, 5, 100 | 3 |
| 5 | 10, 5, 100 | 3 |
| 10 | 10, 100 | 2 |
| 100 | 100 | 1 |
The result is [3, 3, 3, 3, 2, 1]. The starter already builds the vector
and counts per probe. Repair its inner closure so each validator keeps its
own limit after the outer map closure returns.
Why the starter cannot return its closure
In .map(|limit| ...), limit is a local parameter of that invocation.
The inner |x| x <= limit borrows it. Returning the inner closure would
leave that borrow pointing at a local that has gone out of scope, which
Rust rejects with E0373.
Add move to the inner closure to capture the i64 limit by value. Because
i64 is Copy, each validator stores a copy and no longer borrows the
outer closure's local parameter.
move captures the values the closure needs. It does not clone an owned
String, and it does not extend the lifetime of data behind a reference.
Without move, capture behavior is inferred from how the body uses values;
a closure can still take ownership when its body requires it.
Capturing a reference does not own its referent
This closure captures text, whose value is a reference. It remains valid
only while the borrowed string is available:
let owned = String::from("hello");
let text = owned.as_str();
let length = move || text.len();
assert_eq!(length(), 5);
assert_eq!(owned, "hello");
A 'static bound rules out captures containing shorter-lived borrows. It
does not mean a closure must remain alive forever. Boxed trait-object
lifetimes can be written explicitly; a function returning
Box<dyn Fn() -> usize> uses the default 'static object bound, whereas
Box<dyn Fn() -> usize + 'a> can borrow for 'a.
Even allowing a shorter object lifetime cannot make a returned closure
borrow the outer map invocation's expired local. That is why copying the
limit into each validator fixes this exercise.
Capture ownership and call traits are separate
All ordinary closures implement FnOnce. A closure that only reads its
captured value can also implement Fn, allowing repeated shared calls:
let n = 5i64;
let add = move |x: i64| x + n;
assert_eq!(add(1), 6);
assert_eq!(add(2), 7);
The validators read their captured limits, so they implement Fn and can
each be called for all six probes. Owning a capture does not by itself
restrict the closure to one call. Moving a non-Copy capture out of the
body would do that; modifying captured state can require FnMut instead.
The captured state also affects whether the closure itself can be copied:
let n = 5i64;
let add = move |x: i64| x + n;
let another = add;
assert_eq!(another(1), 6);
assert_eq!(add(2), 7);
Here the closure is Copy. A closure owning a String is not Copy,
even if its body only reads that string and supports repeated calls.
Thread-safety bounds are another check: adding move alone does not
guarantee that a closure can be sent to another thread.
Submissions must compile, pass the cases and pass the Rust Clippy gate.
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.