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!

Profile Picture
Senior Advisor, Data Science | [email protected] | + posts

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.

Buy Me a Coffee ✨