🚂 Rust Iterators: The Magic Assembly Line
Imagine a factory where tiny robots only do work when you actually need the product!
🎬 The Story of the Lazy Factory
Picture a chocolate factory with a long assembly line. But this isn’t a normal factory—it’s a lazy factory! The machines only turn on when someone is actually waiting to eat the chocolate at the end.
That’s exactly how Rust iterators work!
- The assembly line = Iterator
- The machines along the line = Iterator adaptors (like
mapandfilter) - Someone eating chocolate at the end = Consuming the iterator
Let’s explore this magical factory!
🎯 What is the Iterator Trait?
Think of the Iterator trait as a promise. Any collection that makes this promise says:
“I can give you items one at a time, until I run out!”
The Simple Rule
Every iterator knows how to do ONE thing: give you the next() item.
// A simple iterator example
let numbers = vec![1, 2, 3];
let mut iter = numbers.iter();
println!("{:?}", iter.next());
// Some(1)
println!("{:?}", iter.next());
// Some(2)
println!("{:?}", iter.next());
// Some(3)
println!("{:?}", iter.next());
// None (empty!)
Why Some and None?
Some(value)= “Here’s your next item!”None= “Sorry, I’m all out!”
It’s like a vending machine. You press the button, and either a snack comes out (Some), or it’s empty (None).
😴 Lazy Evaluation: The Sleepy Workers
Here’s the magic secret of Rust iterators:
Nothing happens until you ask for it!
The Lazy Factory Analogy
Imagine you tell the factory:
- “Paint all chocolates red”
- “Remove the small ones”
- “Wrap them in gold paper”
In a normal factory, workers immediately start doing ALL of this.
In a lazy factory (Rust iterators), workers just… sit there. They only start when someone says “Give me a chocolate!”
let numbers = vec![1, 2, 3, 4, 5];
// This does NOTHING yet!
let lazy_work = numbers.iter()
.map(|x| x * 2)
.filter(|x| *x > 4);
// Still nothing happened!
println!("Workers are sleeping...");
// NOW the work happens!
for num in lazy_work {
println!("{}", num);
}
// Prints: 6, 8, 10
Why is Lazy Good?
- Saves energy - Don’t do work you don’t need
- Works with infinite things - You can have an endless assembly line!
- Faster - Only processes what you actually use
🔧 Iterator Adaptors: The Machine Add-Ons
Iterator adaptors are like adding new machines to your assembly line. Each machine takes items in, does something, and passes them along.
The cool part? You can chain them together!
graph TD A["📦 Original Items"] --> B["🔧 Adaptor 1: map"] B --> C["🔧 Adaptor 2: filter"] C --> D["📦 Final Items"]
Key Adaptors
| Adaptor | What It Does | Analogy |
|---|---|---|
map |
Transform each item | Painting machine |
filter |
Keep only some items | Quality checker |
Let’s explore each one!
🎨 The map Method: The Transformation Machine
map takes every item and transforms it into something else.
Real-Life Analogy
Imagine a machine that takes plain donuts and adds frosting to each one:
- Plain donut goes IN → Frosted donut comes OUT
- Every single donut gets the same treatment
let plain_donuts = vec![1, 2, 3];
// Add 10 "frosting units" to each
let frosted = plain_donuts.iter()
.map(|donut| donut + 10);
// Collect the results
let result: Vec<i32> = frosted.collect();
println!("{:?}", result);
// [11, 12, 13]
More Examples
Double everything:
let nums = vec![1, 2, 3];
let doubled: Vec<i32> = nums.iter()
.map(|x| x * 2)
.collect();
// [2, 4, 6]
Convert to strings:
let nums = vec![1, 2, 3];
let strings: Vec<String> = nums.iter()
.map(|x| x.to_string())
.collect();
// ["1", "2", "3"]
The Golden Rule of map
Same number of items IN = Same number of items OUT
map never removes items. It just changes them!
🚦 The filter Method: The Bouncer
filter is like a bouncer at a club. It looks at each item and decides:
- ✅ “You pass the test, come in!”
- ❌ “Nope, you’re out!”
Real-Life Analogy
A quality checker at a fruit factory:
- Only apples bigger than a certain size get through
- Small apples are rejected
let apples = vec![3, 7, 2, 8, 1, 9];
// Only keep apples bigger than 5
let big_apples: Vec<&i32> = apples.iter()
.filter(|apple| **apple > 5)
.collect();
println!("{:?}", big_apples);
// [7, 8, 9]
The Test Function
filter needs a function that returns true or false:
true→ Keep this item!false→ Throw it away!
Keep only even numbers:
let nums = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<&i32> = nums.iter()
.filter(|x| **x % 2 == 0)
.collect();
// [2, 4, 6]
Keep only positive numbers:
let nums = vec![-3, -1, 0, 2, 5];
let positives: Vec<&i32> = nums.iter()
.filter(|x| **x > 0)
.collect();
// [2, 5]
⛓️ Chaining: Build Your Dream Factory!
The REAL power comes from chaining adaptors together!
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result: Vec<i32> = numbers.iter()
.filter(|x| **x % 2 == 0) // Keep evens
.map(|x| x * 10) // Multiply by 10
.collect();
println!("{:?}", result);
// [20, 40, 60, 80, 100]
What Just Happened?
graph TD A["[1,2,3,4,5,6,7,8,9,10]"] --> B["filter: keep evens"] B --> C["[2, 4, 6, 8, 10]"] C --> D["map: multiply by 10"] D --> E["[20, 40, 60, 80, 100]"]
Step by step:
- Start with 1-10
filterkeeps only even numbers: 2, 4, 6, 8, 10mapmultiplies each by 10: 20, 40, 60, 80, 100
🏁 Consuming Iterators: Starting the Factory
Remember: iterators are lazy! To actually get results, you need to consume them.
Common Consumers
| Consumer | What It Does |
|---|---|
collect() |
Gather all items into a collection |
for loop |
Process each item one by one |
sum() |
Add all numbers together |
count() |
Count how many items |
let nums = vec![1, 2, 3, 4, 5];
// Collect into a new vector
let doubled: Vec<i32> = nums.iter()
.map(|x| x * 2)
.collect();
// Sum all values
let total: i32 = nums.iter().sum();
// Count items
let count = nums.iter().count();
🎮 Putting It All Together
Let’s build a complete example:
Problem: Find all words longer than 3 letters, and make them UPPERCASE.
let words = vec!["hi", "hello", "rust", "is", "fun"];
let result: Vec<String> = words.iter()
.filter(|word| word.len() > 3)
.map(|word| word.to_uppercase())
.collect();
println!("{:?}", result);
// ["HELLO", "RUST"]
Breaking It Down
- Start:
["hi", "hello", "rust", "is", "fun"] - Filter (len > 3):
["hello", "rust"] - Map (uppercase):
["HELLO", "RUST"]
🧠 Key Takeaways
| Concept | Remember This |
|---|---|
| Iterator trait | The promise to give items one at a time |
| Lazy evaluation | Nothing happens until you consume |
| Iterator adaptors | Machines that transform the assembly line |
map |
Transform every item (same count) |
filter |
Keep only items that pass the test |
🚀 You Did It!
You now understand Rust iterators like a pro! Remember:
Iterators are like a lazy chocolate factory—the machines only start when someone is hungry!
Go forth and chain those adaptors! 🎉
