For loops and ownership

It seems that a for loop takes ownership of the thing it's iterating over. For example, the following code won't compile:

let r = 1..4;
for i in r {
    println!("{}", i);
}
for i in r {
    println!("{}", i);
}

The second for loop causes an error use of moved value: r.

But I don't really understand why it has to be this way. This code doesn't modify the range itself or any of the integers it yields to the for loop. In practice I can work around it by using r.clone() but this seems ugly. Is there some way to do this with immutable references instead?

1 Like

You do need a mutable reference and yes it does modify the range itself (range implements iterator and iterating over a range consumes the range).

You can do:

let mut r = 1..4;
for i in &mut r {
    println!("{}", i);
}
for i in &mut r {
    println!("{}", i);
}

or

let mut r = 1..4;
for i in r.by_ref() {
    println!("{}", i);
}
for i in r.by_ref() {
    println!("{}", i);
}

but this is kind of pointless because the second loop will never run.

3 Likes

Ah, that also explains why I have call .iter() on vectors.

But this still seems like an odd design to me. In many other languages, a for loop expects an iterable object, which can produce an iterator for the loop. So the argument to the for loop is not itself consumed during iteration.

Any idea why Rust does things differently?

Rust for loops actually do take iterables (specifically, structs that implement the IntoIterator trait).

IntoIterator is defined as follows (note that into_iter takes self by value):

pub trait IntoIterator where Self::IntoIter::Item == Self::Item {
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

Additionally, rust defines a blanket IntoIterator impl for Iterators such that into_iter called on an Iterator returns itself. See: std::iter - Rust

FYI, vectors also implement IntoIterator twice:

  1. The implementation on &Vec returns an iterator that yields references to the values (equivalent to calling iter).
  2. The implementation on Vec (by value) returns an iterator that yields the elements by value.

So, for i in vec.iter() is equivalent to for i in &vec.

1 Like

Not only twice, but also for &mut Vec.

Thanks, that clears things up. I didn't realize that for i in vec was allowed since I've mostly been writing code with map and filter, where I do need vec.iter().