Rust Learning Path — Chapter 18. Closures and iterators let you express transformations clearly while still compiling to efficient native code. They are everywhere in idiomatic Rust—from collection processing to async streams and application configuration.
1. Closures are anonymous functions
fn main() { let add = |a: i32, b: i32| a + b; println!("{}", add(2, 3)); }Closure parameter and return types can often be inferred from usage.
2. Closures can capture their environment
fn main() { let prefix = String::from("user-"); let format_id = |id: u64| format!("{prefix}{id}"); println!("{}", format_id(42)); }Unlike a normal fn, a closure can borrow or take ownership of surrounding values.
3. Fn, FnMut and FnOnce
| Trait | Typical behavior |
|---|---|
| Fn | Can be called repeatedly without mutating captured state. |
| FnMut | May mutate captured state. |
| FnOnce | May consume captured values and therefore might be callable only once. |
fn run_twice<F>(mut action: F) where F: FnMut(), { action(); action(); }4. move closures
move forces a closure to take ownership of captured values, which is especially important when sending work to another thread:
use std::thread; fn main() { let message = String::from("hello"); let handle = thread::spawn(move || { println!("{message}"); }); handle.join().unwrap(); }5. Iterators are lazy
Calling iter() does not immediately process every element. Iterator adapters build a pipeline, and a consuming adapter such as collect, sum or a for loop drives it.
let values = vec![1, 2, 3, 4]; let doubled = values.iter().map(|n| n * 2);6. iter, iter_mut and into_iter
| Method | Produces | Ownership |
|---|---|---|
| iter() | References | Collection remains usable |
| iter_mut() | Mutable references | Collection remains owned |
| into_iter() | Owned items | Usually consumes the collection |
7. map
let ports = vec![80, 443, 8080]; let labels: Vec<String> = ports .iter() .map(|port| format!("tcp:{port}")) .collect();8. filter
let numbers = vec![1, 2, 3, 4, 5, 6]; let evens: Vec<_> = numbers .into_iter() .filter(|n| n % 2 == 0) .collect();9. filter_map
filter_map is useful when each input may or may not produce an output:
let raw = vec!["10", "bad", "20"]; let parsed: Vec<i32> = raw .into_iter() .filter_map(|s| s.parse().ok()) .collect();10. find, any and all
let ports = [22, 80, 443]; assert!(ports.iter().any(|p| *p == 443)); assert!(ports.iter().all(|p| *p > 0)); let http = ports.iter().find(|p| **p == 80);11. fold
fold combines values into an accumulator:
let total = [10, 20, 30] .into_iter() .fold(0, |acc, n| acc + n);12. enumerate and zip
for (index, value) in ["api", "db", "cache"].iter().enumerate() { println!("{index}: {value}"); } let names = ["api", "worker"]; let ports = [8080, 9090]; for (name, port) in names.iter().zip(ports) { println!("{name} -> {port}"); }13. A practical data pipeline
#[derive(Debug)] struct Server { name: String, cpu: u8, } fn main() { let servers = vec![ Server { name: "api-1".into(), cpu: 40 }, Server { name: "api-2".into(), cpu: 92 }, Server { name: "db-1".into(), cpu: 75 }, ]; let overloaded: Vec<String> = servers .into_iter() .filter(|s| s.cpu >= 80) .map(|s| s.name) .collect(); println!("{overloaded:?}"); }14. Avoid unnecessary allocations
You do not always need collect(). If the next operation can consume the iterator directly, keep it lazy:
let total: i32 = (1..=100) .filter(|n| n % 2 == 0) .sum();15. Common mistakes
- Using into_iter() and then trying to reuse a moved collection.
- Collecting after every adapter instead of keeping one pipeline.
- Writing a complex chain that is harder to read than a simple loop.
- Cloning inside closures when borrowing would work.
Mini lab
Build a log analyzer that accepts a vector of status-code strings, parses valid values, keeps only errors >= 400, groups or counts them, and prints a summary. Use filter_map, filter, and collect.
Key takeaways
Closures package behavior and can capture surrounding values. Iterators provide composable, lazy data processing. The important skill is choosing ownership correctly and keeping pipelines readable.
Join the discussion
0 CommentsAsk questions, add context, or share what worked.
More from Melochip Editorial
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026
Learn Kubernetes security best practices with RBAC, Secrets, ServiceAccounts, Pod securityContext, NetworkPoli...
6 min read · Sep 17, 2026Related articles
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026
Learn Kubernetes security best practices with RBAC, Secrets, ServiceAccounts, Pod securityContext, NetworkPoli...
6 min read · Sep 17, 2026
Learn Kubernetes monitoring and logs with kubectl logs, events, metrics-server, top commands, Prometheus, Graf...
5 min read · Sep 17, 2026Stay in the loop. Get new Melochip articles
Receive Melochip article updates when new articles are available. Explicit opt-in only.
No comments yet. Start the discussion.