In this short tutorial, we’ll look at Iterators in Rust, and how to work with for loops using iterators — both mutably and immutably. Iterators, like the name implies, allow us to loop through list list like
data structures in rust. It can be a Vector, a Hashmap, or even a custom type that can implement Iterator traits.
Since we are working with Vectors or references in general ( which are stored on heap ), we would have to follow rules for references even when dealing with primitive types such as integers and floats. Let’s take two simple examples to understand this.
For this tutorial, we’ll create a new rust Project as a library.
cargo new --lib map-iterator-demo
Let’s add the following code to src/lib.rs
file .This is the starting point of our library.
We’ll create a new vector with 10 elements and create a new iterator for the list.
mod iterators_demo {
pub fn multiply( ){
// define a vector of 10 integers.
let nums = vec![1,2,3,4,5,6,7,8,9,10];
// create an iterator to iterate over this list.
let nums_iterator =nums.iter();
}
}
Iterators in Rust allow us to loop through the list and perform operations / mutate the list if required. By default, immutable references are returned as values for iterators.
Additionally, iterators in rust are lazy by default. Just by defining an iterator variable, our code would have no effect. In fact, try compiling this and see what happens in this case. We need to perform operations on this iterator to start consuming the list.
warning: unused variable: `nums_iterator`
--> src/lib.rs:9:13
|
9 | let nums_iterator =nums.iter();
| ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_nums_iterator`
|
= note: `#[warn(unused_variables)]` on by default
We know that vectors have their value stored on the Heap. Additionally, since iterators on vectors are implemented by standard library using Generics, we would get an immutable reference whenever we go through the list. We cannot modify the value / update the it the list when working with.iter()
method. But rust provides a way for us to get a mutable iterator as well. Let’s look at how to use iterators and print the list using for loops.
Let’s print our list before we dive into mutating the list. We create a new for loop using the for … in
syntax. Try updating our function as shown below.
Let’s print our list before we dive into mutating the list. We create a new for loop using the for … in
syntax. Try updating our function as shown below.
pub fn multiply( ){
// define a vector of 10 integers.
let nums = vec![1,2,3,4,5,6,7,8,9,10]; // create an iterator to iterate over this list.
let nums_iterator =nums.iter(); for num in nums_iterator {
println!(" Num value {} " , num)
}
}warning: `map-iterator-demo` (lib test) generated 1 warning
Finished test [unoptimized + debuginfo] target(s) in 0.00s
Running unittests src/lib.rs (target/debug/deps/map_iterator_demo-bd3479416081810b)
Num value 1
Num value 2
Num value 3
Num value 4
Num value 5
Num value 6
Num value 7
Num value 8
Num value 9
Num value 10
We saw that iterators return references to the original elements in the list. We know what these references reside on the heap. So it’s possible to modify these values in place if we had a mutable reference to the elements.
This is where mutable iterators come into picture. In the example below we see how to modify list values in place using mutable iterators.
pub fn multiply( ){
// define a vector of 10 integers.
let mut nums = vec![1,2,3,4,5,6,7,8,9,10]; // create an iterator to iterate over this list.
let mut nums_iterator =nums.iter_mut(); for num in nums_iterator {
*num *= 12;
} for i in nums.iter() {
println!("New value {} " , i)
}
}
Notice the differences in the code. First, we make the vector mutable. Then we call the iter_mut()
which gives a mutable reference to the element. And then we update the value to multiply it by 12. These steps are mandatory to update iterators
We then print the original list. Notice how each element now is updated to be multiplied by 12.
warning: `map-iterator-demo` (lib test) generated 2 warnings
Finished test [unoptimized + debuginfo] target(s) in 0.00s
Running unittests src/lib.rs (target/debug/deps/map_iterator_demo-bd3479416081810b)
New value 12
New value 24
New value 36
New value 48
New value 60
New value 72
New value 84
New value 96
New value 108
New value 120Process finished with exit code 0
In general mutability of an iterator also follows the same patterns as variable mutability. And the same holds for any complex data type. Iterators are powerful, and at some point, we may even need to create one in our programs by extending the Iterator
trait in Rust. Their extensibility and performance allow us to write powerful programs in the simplest of ways.