Iterators
The iterator pattern allows you to perform some task on a sequence of items in turn. Iterators provide a way to traverse, transform, or consume elements efficiently without needing to manually manage indices or loops.
Rust iterators are lazy by default, meaning they don’t perform operations until explicitly consumed.
Closures and iterators are Rust features inspired by functional programming language ideas. They contribute to Rust’s capability to clearly express high-level ideas at low-level performance. The implementations of closures and iterators are such that runtime performance is not affected. This is part of Rust’s goal to strive to provide zero-cost abstractions. - Comparing Performance: Loops vs. Iterators
Iterator Trait
An iterator is any type that implements the Iterator trait. The Iterator trait has one required method:
fn next(&mut self) -> Option<Self::Item>;next returns the next item in the sequence wrapped in Some or None if the iterator is exhausted.
Advances the iterator and returns the next value.
Returns
Nonewhen iteration is finished. Individual iterator implementations may choose to resume iteration, and so callingnext()again may or may not eventually start returningSome(Item) again at some point.
Example:
fn main() {
let numbers = vec![1, 2, 3];
let mut iter = numbers.iter();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
}Output:
Consumer Adapters
The Iterator trait has a number of different methods with default implementations provided by the standard library. Methods that call next are called consuming adapters, because calling them uses up the iterator.
Some of commonly used consumer adapters methods: count, sum, fold, product, collect, etc. You can find more of the provided method here: https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods.
Examples
Sum
Sums the elements of an iterator.
Takes each element, adds them together, and returns the result.
An empty iterator returns the zero value of the type.
Example:
Fold
Folds every element into an accumulator by applying an operation, returning the final result.
fold()takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.The initial value is the value the accumulator will have on the first call.
After applying this closure to every element of the iterator,
fold()returns the accumulator.Example:
Iterator Adapters
Iterator adapters are methods defined on the Iterator trait that don’t consume the iterator. Instead, they produce different iterators by changing some aspect of the original iterator.
Some of commonly used iterator adapters method: map, filter, take, skip, enumerate, etc. Again, you can find more of the provided method here: https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods.
Examples
Map
Takes a closure and creates an iterator which calls that closure on each element.
map()transforms one iterator into another, by means of its argument: something that implementsFnMut. It produces a new iterator which calls this closure on each element of the original iterator.If you are good at thinking in types, you can think of
map()like this: If you have an iterator that gives you elements of some typeA, and you want an iterator of some other typeB, you can usemap(), passing a closure that takes anAand returns aB.
Filter
Creates an iterator which uses a closure to determine if an element should be yielded.
Given an element the closure must return
trueorfalse. The returned iterator will yield only the elements for which the closure returnstrue.Example:
The Power of collect()
collect()The collect method is part of the Iterator trait and is used to transform an iterator into a collection. It relies on the FromIterator trait to determine how to build the target type.
Example of Vec::from_iter:
collect() can take anything iterable, and turn it into a relevant collection. This is one of the more powerful methods in the standard library, used in a variety of contexts.
The most basic pattern in which collect() is used is to turn one collection into another. You take a collection, call iter on it, do a bunch of transformations, and then collect() at the end.
Example of collect:
So in here Rust will check for the target type when
collectis called.As we can see the function
take_vecis expecting a typeVec<i32>.Rust compiler then will infer the target of
collectisVec<i32>.And because
VechasFromIteratortrait, it will callVec::from_iterfunction and produce the result.
Why collect Fails Sometimes?
collect Fails Sometimes?There are various reason that sometimes collect cannot built the targeted type, some's are:
Ambiguous Target Type: If the target type is not clear from the context.
Type Mismatch: If the iterator produces items that cannot be converted to the target type.
Unsupported Target Type: If the target type doesn’t implement FromIterator.
Though, we can implement
FromIteratorif we have our own custom type like this:
iter vs iter_mut vs into_iter
iter vs iter_mut vs into_iter.iter()
.iter()Returns an iterator of references (
&T) over the elements of the collection.The collection is not consumed.
Cannot modify the original collection’s elements (unless combined with
.iter_mut()).
Example:
.iter_mut()
.iter_mut()Returns an iterator of mutable references (
&mut T) over the elements of the collection.For modifying elements of a collection in place.
Example:
.into_iter()
.into_iter()Consumes the collection and returns an iterator of owned values (
T).Moving ownership of each element.
After calling
.into_iter(), the original collection cannot be used anymore because ownership is transferred to the iterator.
Exmaple:
References
Last updated