Method chaining in Rust works because most iterator adapters return a new iterator, so you can stack operations without intermediate variables. It's the same idea as piping commands in a shell — each step transforms the data and passes it along.

The Pattern

let result: Vec<String> = names
    .iter()
    .filter(|name| name.len() > 3)
    .map(|name| name.to_uppercase())
    .collect();

Each method consumes the previous iterator and produces a new one. Nothing actually runs until .collect() (or another consumer) is called — this is because iterators are lazy.

Imperative Equivalent

The same logic without chaining:

let mut result: Vec<String> = Vec::new();
for name in &names {
    if name.len() > 3 {
        result.push(name.to_uppercase());
    }
}

Both produce the same output. The chained version is more declarative — it says what to do rather than how to do it. The imperative version is easier to step through with a debugger though, so it's a trade-off.

Why It Works

Methods like .filter() and .map() return impl Iterator, which means you can call another iterator method on the result. The compiler fuses these into a single pass over the data — no intermediate collections are created.

  • Rust Iter — Core iterator concepts and adapter methods