Iterators
Last updated
Last updated
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. -
An iterator is any type that implements the Iterator trait. The Iterator trait has one required method:
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
None
when 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:
Output:
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.
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:
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 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.
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 implements FnMut
. 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 type A
, and you want an iterator of some other type B
, you can use map()
, passing a closure that takes an A
and returns a B
.
Creates an iterator which uses a closure to determine if an element should be yielded.
Given an element the closure must return true
or false
. The returned iterator will yield only the elements for which the closure returns true
.
Example:
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 collect
is called.
As we can see the function take_vec
is expecting a type Vec<i32>
.
Rust compiler then will infer the target of collect
is Vec<i32>
.
And because Vec
has FromIterator
trait, it will call Vec::from_iter
function and produce the result.
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 FromIterator
if we have our own custom type like this:
iter
vs iter_mut
vs into_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()
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()
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:
Some of commonly used consumer adapters methods: count
, sum
, fold
, product
, collect
, etc. You can find more of the provided method here: .
Some of commonly used iterator adapters method: map
, filter
, take
, skip
, enumerate
, etc. Again, you can find more of the provided method here: .