Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

for-loop

rust

I'm confused by how Rust for loops work. Consider the following:

#![feature(core_intrinsics)]

fn print_type_of<T>(_: T) {
    println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}

fn main() {
    let nums = vec![1, 2, 3];
    for num in &nums { print_type_of(num); }
    for num in  nums { print_type_of(num); }
}

It outputs the following:

&i32
&i32
&i32
i32
i32
i32

What does it mean to pass in a vector into for versus a reference to a vector? Why, when you pass in a reference, do you get a reference to the items and when you pass in an actual vector, you get the actual items?

like image 256
Ana Avatar asked Mar 27 '17 00:03

Ana


1 Answers

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:

  • What is the difference between iter and into_iter?
  • Why can I iterate over a slice twice, but not a vector?
like image 80
Shepmaster Avatar answered Oct 14 '22 04:10

Shepmaster