Ownership in Closures

Closures can capture variables from their surrounding scope. How they capture — by reference or by value — follows Rust's ownership rules, and the distinction matters when a closure is called multiple times.

The error we hit

When building the pixel buffer, the first attempt used an iterator chain:

#![allow(unused)]
fn main() {
// FAILS — cpu.display is moved on the first iteration, gone on the second
let buffer: Vec<u32> = (0..HEIGHT)
    .flat_map(|y| {
        (0..WIDTH).map(move |x| {
            if cpu.display.pixel(x, y) { 0xFFFFFFFF } else { 0xFF000000 }
        })
    })
    .collect();
}

The compiler error: "cannot move out of cpu.display, a captured variable in an FnMut closure."

flat_map calls its closure once per row — 32 times. The inner move closure attempts to take ownership of cpu.display on every call. But ownership can only transfer once. After the first call, cpu.display is gone.

The fix: a plain loop borrows by reference

#![allow(unused)]
fn main() {
let mut buffer = vec![0u32; WIDTH * HEIGHT];
for y in 0..HEIGHT {
    for x in 0..WIDTH {
        buffer[y * WIDTH + x] = if cpu.display.pixel(x, y) {
            0xFFFFFFFF
        } else {
            0xFF000000
        };
    }
}
}

A for loop borrows cpu.display by reference on each iteration — no ownership transfer, no problem.

Why move exists

move closures take ownership of captured variables. They are necessary when the closure outlives the scope where it was created — for example, spawning a thread:

#![allow(unused)]
fn main() {
let data = vec![1, 2, 3];
std::thread::spawn(move || {
    println!("{:?}", data);  // data is moved into the thread
});
}

Without move, the thread could outlive data — Rust rejects this at compile time.

Three ways closures capture

ModeSyntaxWhen used
Immutable referencedefaultclosure only reads
Mutable referencedefault when neededclosure modifies
By value (move)move || { }closure must own the value

Rust chooses the least restrictive mode automatically. move overrides this.

The rule

A value can only be moved once. If a closure is called multiple times (like inside flat_map), it cannot move the same value on each call — the value would be gone after the first. References are Copy and can be used any number of times; owned values cannot.


In the emulator: This error was encountered when building the render loop in main.rs. The plain for loop solution is what lives in the final code. See The Main Loop for the full context.