What is the Difference Between iter, into_iter and iter_mut in Rust?

by | Programming, Rust, Tips

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:

Have fun and happy researching!

Research Scientist at Moogsoft | + posts

Suf is a research scientist at Moogsoft, specializing in Natural Language Processing and Complex Networks. Previously he was a Postdoctoral Research Fellow in Data Science working on adaptations of cutting-edge physics analysis techniques to data-intensive problems in industry. In another life, he was an experimental particle physicist working on the ATLAS Experiment of the Large Hadron Collider. His passion is to share his experience as an academic moving into industry while continuing to pursue research. Find out more about the creator of the Research Scientist Pod here and sign up to the mailing list here!

Follow the Research Scientist Pod on Social media!