In Rust iter()
returns an iterator of slices, into_iter()
returns an iterator from a value, and iter_mut()
returns an iterator that allows modifying each value. This tutorial will detail the Iterator and IntoIterator traits and the differences between the three iterator methods.
The Iterator Trait
All iterators in Rust implement the Iterator
trait defined in the Rust standard library.
pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; }
The above code tells us that when we implement the Iterator
trait, we also have to define the Item type returned by the next
method. The next
method returns the next item in the iteration wrapped in Some. Once we reach the end of the iteration, the method returns None
.
We need a mutable reference to self
because a call to the next
method changes the internal state of the iterator to determine where it is in the sequence.
There is a trait in the standard library for converting something into a library called IntoIterator
. This trait comes with a single method, into_iter
which converts the thing that implements IntoIterator
into an iterator.
Any type that implements IntoIterator
is called an iterable. We implicitly implement the IntoIterator
trait when we define a for loop, for example:
let pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; for pizza in pizzas { println!("{}", pizza); }
The above code desugars (expanding out the syntax to greater complexity) to:
let pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; let mut iterator = (pizzas).into_iter(); loop { match iterator.next() { Some(x) => println!("{}", x), None => break, } }
margherita pepperoni hawaiian four cheeses
We can also pass the iterator directly to the for
loop.
let pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; let iterator = (pizzas).into_iter(); for pizza in iterator { println!("{}", pizza); }
margherita pepperoni hawaiian four cheeses
The use of iterators in a for loop is suitable for types like Range.
Differences Between iter(), into_iter() and iter_mut()
The into_iter()
method is a generic method to obtain an iterator. We implicitly call the into_iter()
method during a for
loop. The iterator can yield values, immutable or mutable references depending on the context. For example, we can have the following examples:
into_iter() with collection as a value
let pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; for pizza in pizzas { println!("{}", pizza); }
In the above code, we provide the collection as a value and the implicit call to into_iter()
returns an iterator that takes ownership of the collection and yields the items by value.
into_iter() with mutable reference to a collection
let mut pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; for pizza in &mut pizzas { println!("{}", pizza); }
In the above code, we provide a mutable reference to the collection. The implicit call to into_iter()
returns an iterator that yields mutable references to the items.
into_iter() with a shared reference to a collection
let pizzas = vec!["margherita", "pepperoni", "hawaiian", "four cheeses"]; for pizza in &pizzas { println!("{}", pizza); }
In the above code, we provide a shared reference to the collection. The implicit call to into_iter()
returns an iterator that yields shared references to its items.
The methods iter()
and iter_mut()
provide a way to create iterators explicitly.
The iter()
method always returns an iterator that yields immutable references to its items.
The iter_mut()
method always returns an iterator that yields mutable references to its items.
Test Functions For iter(), into_iter(), and iter_mut()
We can use test functions to demonstrate the differences between iter()
into_iter()
, and iter_mut()
:
iter() method
The iter()
method returns an iterator over the slice.
#[test] fn iter_demo() { let v = vec![1, 2, 3]; let mut v_iter = v.iter(); // iter() returns an iterator of slices assert_eq!(v_iter.next(), Some(&1)); assert_eq!(v_iter.next(), Some(&2)); assert_eq!(v_iter.next(), Some(&3)); assert_eq!(v_iter.next(), None) }
into_iter() method
The into_iter()
method returns an iterator from a value.
#[test] fn into_iter_demo() { let v = vec![1, 2, 3]; let mut v_iter = v.into_iter(); // into_iter() returns an iterator from a value assert_eq!(v_iter.next(), Some(1)); assert_eq!(v_iter.next(), Some(2)); assert_eq!(v_iter.next(), Some(3)); assert_eq!(v_iter.next(), None) }
iter_mut() method
The iter_mut()
returns an iterator that allows modifying each value.
#[test] fn iter_mut_demo() { let mut v = vec![1, 2, 3]; let mut v_iter = v.iter_mut(); // iter_mut() returns an iterator that allows modifying of each value assert_eq!(v_iter.next(), Some(&mut 1)); assert_eq!(v_iter.next(), Some(&mut 2)); assert_eq!(v_iter.next(), Some(&mut 3)); assert_eq!(v_iter.next(), None) }
Each of these test functions runs successfully.
Summary
Congratulations on reading to the end of this tutorial! For further reading on Rust, go to the articles:
- How to Concatenate Strings in Rust
- How to Do Bubble Sort in Rust
- How to Split a String in Rust
- How to Get an Absolute Value in Rust
Have fun and happy researching!
Suf is a senior advisor in data science with deep expertise in Natural Language Processing, Complex Networks, and Anomaly Detection. Formerly a postdoctoral research fellow, he applied advanced physics techniques to tackle real-world, data-heavy industry challenges. Before that, he was a particle physicist at the ATLAS Experiment of the Large Hadron Collider. Now, he’s focused on bringing more fun and curiosity to the world of science and research online.