#174 May 31, 2026

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:

1
2
let zeros: Vec<u32> = vec![0; 5];
assert_eq!(zeros, vec![0, 0, 0, 0, 0]);

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:

1
2
3
use std::sync::Mutex;
// error[E0277]: the trait `Clone` is not implemented for `Mutex<u32>`
// let locks = vec![Mutex::new(0); 10];

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:

1
2
3
4
5
use std::iter;
use std::sync::Mutex;

let locks: Vec<Mutex<u32>> = iter::repeat_with(|| Mutex::new(0)).take(10).collect();
assert_eq!(locks.len(), 10);

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:

1
2
3
4
5
use std::iter;

let mut n = 0;
let counts: Vec<u32> = iter::repeat_with(|| { n += 1; n }).take(5).collect();
assert_eq!(counts, vec![1, 2, 3, 4, 5]);

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:

1
2
3
4
5
use std::iter;

let buffers: Vec<Vec<i32>> = iter::repeat_with(Vec::new).take(3).collect();
assert_eq!(buffers.len(), 3);
assert!(buffers.iter().all(|b| b.is_empty()));

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.

← Previous 173. NonZeroU32 and Friends — Encode an Invariant and Shrink Option for Free Next → 175. PathBuf::push — When an Absolute Argument Wipes Your Base Path