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.
Related
- Rust Iter — Core iterator concepts and adapter methods