174. iter::repeat_with — Build N Fresh Things When vec![] Won't Clone
vec![Mutex::new(0); 10] — won’t compile. Mutex isn’t Clone. iter::repeat_with(|| Mutex::new(0)).take(10).collect() builds ten fresh ones instead.
The vec![x; n] trap
The vec![x; n] macro is the obvious way to build a Vec of n copies. It works by cloning x n times:
| |
That’s fine for u32. It falls apart the moment you want something that isn’t Clone, or something where cloning gives you the wrong behavior — a counter, a channel endpoint, an Arc<Mutex<...>> you wanted to be separate locks:
| |
The fix: iter::repeat_with
iter::repeat_with(f) takes a closure and yields f() every time the iterator is polled. Combine with take(n).collect() and you’ve got a builder that calls the closure exactly n times — no Clone required:
| |
Each Mutex is freshly constructed. They’re independent locks, not ten handles to the same one.
Stateful closures work too
Because the closure is FnMut, it can capture and mutate state — handy for counters, RNG-like generators, or anything where each element depends on the previous call:
| |
Try that with vec![...; 5] and you’d get five copies of the same number.
The function-pointer form
When the closure is just a constructor call, you can pass the function directly — no || needed:
| |
Vec::new here is a zero-argument function pointer, exactly what repeat_with wants.
When to reach for it
Any time you’d write a for loop to push n fresh items into a Vec, or any time vec![x; n] rejects you because the element isn’t Clone. It’s also the idiomatic way to seed parallel structures: repeat_with(|| Arc::new(Mutex::new(...))).take(workers).collect() gives each worker its own lock.
Available since Rust 1.28, lives in core::iter, no allocation overhead beyond the Vec itself.