In this lesson, you will learn how to encapsulate complex data generation logic into reusable, domain-specific iterator adapters that integrate seamlessly into Rust's functional pipelines.
The Power of Custom Adapters
While standard adapters like .map() or .filter() are powerful, they often lead to "logic clutter" when your business domain requires complex state transitions or data generation. By creating a custom struct that implements the Iterator trait, you can abstract away messy implementation details, making your iterator chains read like natural language.
Building a Domain-Specific Iterator
Suppose you are building a simulation that needs to generate a sequence of decaying values. Instead of writing a complex closure every time, you can define a custom adapter:
struct Decayer {
current: f64,
factor: f64,
}
impl Iterator for Decayer {
type Item = f64;
fn next(&mut self) -> Option<Self::Item> {
self.current *= self.factor;
if self.current < 0.01 { None } else { Some(self.current) }
}
}
Now, you can turn this into a reusable adapter by creating a trait extension for any Iterator:
trait IteratorExt: Iterator + Sized {
fn decay(self, factor: f64) -> Decayer {
Decayer { current: 100.0, factor }
}
}
impl<I: Iterator> IteratorExt for I {}
Why this matters
By wrapping logic in a dedicated struct, you gain three major advantages:
- Encapsulation: The internal state (like the
currentvalue andfactor) is hidden from the consumer. - Readability: Your calling code becomes highly expressive, such as
my_data.decay(0.9).take(5).collect(). - Type Safety: You can enforce invariants during the
next()call that would be difficult to manage in a simplemapclosure.
Custom adapters allow you to lift your code from simple data transformations to building a domain-specific "language" of data processing. Because Rust’s Iterator trait relies on lazy evaluation, your custom logic remains as efficient as hand-written loops, benefiting from zero-cost abstractions.
Key Takeaway: Custom iterator adapters allow you to hide complex state and generation logic behind clean, expressive method chains that compose perfectly with the rest of the Rust iterator ecosystem.