Iterator & Extension Traits
Extension traits add methods to existing types (str, Iterator, third-party types) without wrapping. Iterator adapters compose zero-cost data pipelines.
Recipe
trait IterExt: Iterator {
fn collect_vec(self) -> Vec<Self::Item> where Self: Sized { self.collect() }
}
impl<I: Iterator> IterExt for I {}When to reach for this: Ergonomic helpers on types you do not own; reusable iterator utilities.
Working Example
trait StrExt {
fn trimmed_nonempty(&self) -> Option<&str>;
}
impl StrExt for str {
fn trimmed_nonempty(&self) -> Option<&str> {
let t = self.trim();
if t.is_empty() { None } else { Some(t) }
}
}
let ids: Vec<u64> = records.iter().filter_map(|r| r.id).collect();What this demonstrates:
- Extension trait + blanket impl pattern
- Iterator chains lazy until
collect/sum - Prefer std adapters (
filter_map,try_fold) before custom - Keep extension traits in prelude module of your crate
Deep Dive
Blanket impls: impl<T: AsRef<str>> StrExt for T {} for wider coverage. For iterators, Iterator::map + collect beats manual loops unless profiled.
Gotchas
- Orphan rule on foreign types - cannot impl foreign trait for foreign type. Fix: extension trait you own.
- Name collisions - methods clash with std future additions. Fix: prefix methods or use modules.
- Over-custom iterators - reinventing
filter. Fix: use std adapters. - Collect without type - inference failures. Fix:
collect::<Vec<_>>()or turbofish. - Iterator of references clone - hidden allocs. Fix:
copied()for Copy items.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Free functions | No method ergonomics needed | Fluent API desired |
| Wrapper newtype | Many methods on one type | Single helper on str |
itertools crate | Rich adapters | Simple one-liner std |
FAQs
extension vs inherent?
Inherent on your type; extension for others you cannot modify.
itertools when?
tuple_windows, intersperse - add dep when std insufficient.
async iteration?
StreamExt from futures for async pipelines.
blanket impl orphan?
Your trait + your blanket on foreign type is allowed.
method naming?
Avoid std-like names that may conflict later.
performance?
Iterator adapters zero-cost in release when inlined.
try_fold?
Short-circuit Result processing without collect-then-validate.
chain Option?
into_iter() on Option yields 0-1 items.
Polars lazy?
Different iterator model; use Polars expr API not manual IterExt.
test iterators?
Assert on collect::<Vec<_>>() outputs for adapter chains.
Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.