What does it mean to pass in a vector into a `for` loop versus a reference to a vector?

The argument to a for loop must implement IntoIterator. If you check out the docs for Vec, you will see these two implementations of IntoIterator:

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>
}

impl<'a, T> IntoIterator for &'a Vec<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>
}

You get references for &vec and values for vec because that’s how the iterators are defined.

Sometimes, you’ll see these as the more explicit forms: iter or into_iter. The same logic applies; see What is the difference between iter and into_iter?

There’s another form that you will encounter: &mut vec and iter_mut. These return mutable references to the elements in the vector.


As to why the differences at all…

Using a reference to the vector allows you to access the vector after the loop is done. This compiles:

let v = vec![1, 2, 3];
for i in &v {}
for i in &v {}

This does not:

let v = vec![1, 2, 3];
for i in v {}
for i in v {}
error[E0382]: use of moved value: `v`
 --> src/main.rs:4:14
  |
3 |     for i in v {}
  |              - value moved here
4 |     for i in v {}
  |              ^ value used here after move
  |
  = note: move occurs because `v` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait

Ownership-wise, you can’t get a value from a reference unless you clone the value (assuming the type can even be cloned!). This means &vec is unable to yield values that aren’t references.

The implementer of Vec‘s iterator could have chosen to only yield references, but transferring ownership of the elements to the iterator allows the iterator’s consumer to do more things; from a capability perspective it’s preferred.

See also:

Leave a Comment