Loops over collections grow wordy fast. Rust's iterators and closures let you express the same logic in shorter, more declarative pipelines. Here we tour the key Iterator methods, closure syntax, and how they pair together in practice.
Start with Closure Syntax
Closures are anonymous functions that can capture surrounding scope.
fn main() {
let threshold = 50;
let is_high = |score: i32| score > threshold;
println!("Is 70 passing? {}", is_high(70));
}
|score: i32| score > thresholdis the closure.- You can omit types and let the compiler infer them.
- Capture mode—by move, immutable borrow, or mutable borrow—depends on how the body uses the captured values. Add
moveexplicitly when you need to transfer ownership.
A quick heuristic:
- Read-only use captures by shared reference.
- Mutating the capture uses a mutable reference.
- Passing data out of the closure often requires
move.
Core Ideas Behind the Iterator Trait
- Every iterator implements a
nextmethod. - Standard collections (
Vec,HashMap,Range, …) exposeiter,iter_mut, andinto_iterconstructors. - Chainable adapters such as
map,filter,take, andcollectlet you build pipelines.
fn main() {
let scores = vec![45, 67, 88, 52];
let high_scores: Vec<_> = scores
.iter()
.filter(|score| **score >= 60)
.map(|score| format!("Pass: {}", score))
.collect();
println!("{:?}", high_scores);
}
iter()yields immutable references,into_iter()consumes the collection.collect()gathers results into the desired collection type. When inference fails, annotate the target type (let result: Vec<_> = ...).
Ownership Rules and Iterators
iter()yields&Tvalues,iter_mut()yields&mut T,into_iter()yields ownedT.- Mutating through
iter_mut()updates the original collection immediately.
fn main() {
let mut names = vec!["yuna".to_string(), "min".to_string()];
names.iter_mut().for_each(|name| name.make_ascii_uppercase());
println!("{:?}", names);
}
Building Custom Iterators
struct Counter {
current: u32,
max: u32,
}
impl Counter {
fn new(max: u32) -> Self {
Self { current: 0, max }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.max {
None
} else {
self.current += 1;
Some(self.current)
}
}
}
fn main() {
let sum: u32 = Counter::new(5).map(|n| n * 2).sum();
println!("sum = {}", sum);
}
type Itemspecifies what the iterator yields.- Consumers like
sumorproductexhaust the iterator to produce a single value.
Closures Plus Error Handling
fn parse_numbers(lines: &[&str]) -> Result<Vec<i32>, std::num::ParseIntError> {
lines
.iter()
.map(|line| line.trim().parse::<i32>())
.collect()
}
collect()can inferResult<Vec<_>, E>. It stops early on the firstErrand otherwise gathers all values.
Practice in CodeSandbox
The sandbox below uses CodeSandbox's Rust starter. Move the main code into src/main.rs, then compare cargo check and cargo run so you can read the compiler feedback beside the final output.
💬 댓글
이 글에 대한 의견을 남겨주세요