Andrew Cramer
Andrew Cramer

Reputation: 103

Why does a for loop need ownership of a reference?

The following minimal example does not compile:

fn main() {
    let mut v = Vec::new();
    foo(&mut v);
}

fn foo(v_ref: &mut Vec<u8>) {
    for v in v_ref{
        println!("{}", v);
    }
    for v in v_ref{
        println!("{}", v);
    }
}

The compiler suggests to modify the first for loop to this, which does compile.

    for v in &mut *v_ref{

The reasoning given by the compiler is:

move occurs because v_ref has type &mut Vec<u8>, which does not implement the Copy trait

v_ref moved due to this implicit call to .into_iter()

Question 1: Why is this necessary? It's already a reference, shouldn't the for loop give the reference back after we're done with it?

Question 2: Why does the fix work? As I understand it, the new mutable reference should invalidate the old one.

Upvotes: 0

Views: 234

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 71585

Why is this necessary? It's already a reference, shouldn't the for loop give the reference back after we're done with it?

for loops desugar into (roughly):

{
    let iterator = IntoIterator::into_iter(iter);
    while let Some(v) = Iterator::next(&mut iterator) { ... }
}

Notice the IntoIterator::into_iter(). As explained in Do mutable references have move semantics?, mutable references, as opposed to shared references, are not Copy and thus once you move them you cannot use them anymore. Usually, the compiler inserts a reborrow, &mut *reference that creates a new reference from the existing one and allows the existing one to still be used (after the new is no longer used), but this only happens when it is known without inference that the type is a mutable reference, and here we need inference to determine that.

Why does the fix work? As I understand it, the new mutable reference should invalidate the old one.

You can think of references as a stack; each reference created from existing one pushes an item on the stack, and when we finished using it we pop it. &mut *reference creates a new reference, whose lifetime can be shorter than the original reference and allows the original reference to be used again after the end of the lifetime of the new reference.

Upvotes: 5

Related Questions