Difference Between iter() and into_iter() in Rust

1. Introduction

The Rust language provides iterators that make complex data structures easy to traverse, transform, and filter. Common iterators in Rust include iter(), iter_mut(), and into_iter(). Iter() and into_iter() are provided for different use cases.

In this tutorial, we’ll learn the basics of iter() and into_iter(). Finally, we’ll see an example use case and when to use any of the iterators.

2. Understanding Iterator

An iterator is an object that allows us to traverse a sequence of elements. It returns items one by one using the next() method. Iterators are lazy, i.e., they do not execute until an operation like for, collect, etc. consumes them.

Common iterators include:

  • iter() – allows us to iterate over an immutable reference of a sequence of an element
  • iter_mut() – allows us to iterate over a mutable reference of a sequence of an element. It allows modification.
  • into_iter() – allows us to iterate over a moved sequence of an element

3. iter()

Let’s dive deeper into iter() iterator. It allows us to create an iterator over an immutable reference:

let names = vec!["Java", "Rust", "Python"];
let mut len = 0;
for name in names.iter() {
    len = len + name.len();
}
assert_eq!(14,len);

In the code above, we initialize a variable called names. It’s a vector of three elements. Next, we iterate over each element in the vector using the iter() method. By invoking the iter() function on the variable, we can process each element in the vector sequentially.

Finally, we loop over the iterator and sum the length of all elements in the vector together.

Furthermore, it preserves the ownership of the original collection:

println!("{:?}", names);

Here, we can still use the names variable because iter() doesn’t take ownership of the original collection.

Also, it cannot modify elements in the vector during iteration:

for name in names.iter() {
    *name = "C++";
    len = len + name.len();
}

The code above throws an error because we try to change the value of an immutable reference. Even if the names variable is mutable, iter() still cannot modify the content of the vector, in that case, we can use the iter_mut() function.

4. into_iter()

Furthermore, the into_iter() iterator works like iter() however, it takes the ownership of the variable:

let names = vec!["Java", "Rust", "Python"];
let mut len = 0;
for name in names.into_iter() {
    len = len + name.len();
}
assert_eq!(14,len);

The code above is similar to the example in the last section, but we use into_iter() instead. The function takes ownership of the names variable.

In that case, we can’t use the variable again:

 println!("{:?}", names);

The code above throws an error because the names ownership is taken as a result of the into_iter() function.

Furthermore, we can mutate the content of the names variable because we’ve taken the ownership:

for mut name in names.into_iter() {
    name = "C++";
    len = len + name.len();
}

In the code above, we changed the values of the elements into “C++.” Changing the value was easy because we’ve taken ownership of the vector.

5. Key Differences

Both can be applied to a collection of data. However, iter() doesn’t take ownership of the variable that stores the data. Also, the iter() function cannot modify the original data even if the data is mutable. To modify the reference to a mutable variable, we use mut_iter() instead.

Unlike the iter() function, into_iter() takes ownership of the original variable, which implies that the original variable cannot be used again. Since the ownership has been transferred, we can mutate the values in the sequence.

6. Conclusion

In this tutorial, we learned the basics of the iter() and iter_into() functions with examples. We also saw the difference between the two functions and when to use either of them.

We can use iter() when we need read-only access to each element and iter_mut() to modify a mutable reference. Finally, we can use into_iter() when we need to gain ownership of each element in a collection.