Home > Enterprise >  How does for loop implement into_iter
How does for loop implement into_iter

Time:05-17

Follow up this question which is marked as duplicate as question here.

The concept of how for loop implement into_iter keeps haunt me down and this answer kinda creates more question to me like the terms of re-borrow, which is not mentioned in rust official document at all except in one place.

To my understanding of this comment, when vec is a mutable reference, for i in vec.into_iter() is actually for i in (&mut *vec).into_iter() behind the scene.

How about for i in vec, is it actually i in vec.into_iter()? Is there any place that have more detail on how for loop is implemented and how re-borrowing gets triggered and how it works?

Code for reference:

fn question1_1(vec: &mut [i32]) {
    for &mut item in vec.into_iter() {
        println!("{}", item);
    }
    for &mut item in vec { // --- `vec` moved due to this implicit call to `.into_iter()`
        println!("{}", item);
    }
    vec; // Error: move occurs because `vec` has type `&mut [i32]`, which does not implement the `Copy` trait
}

pub fn main() {
    let mut s = vec![1, 2, 3];
    question1_1(&mut s);

CodePudding user response:

for loop is desugared into (this can be seen by inspecting the HIR):

{
    let _t = match IntoIterator::into_iter(iter) {
        mut iter => loop {
            match Iterator::next(&mut iter) {
                None => break,
                Some(i) => {
                    // Body
                }
            }
        },
    };
    _t
}

Specifically, the iterable is transformed into an iterator by executing IntoIterator::into_iter(iterable). Each trait, including IntoIterator, has a hidden Self generic parameter, and so this is actually IntoIterator::<_>::into_iter(iterable). From reborrowing POV, this is similar to:

fn foo<T>(v: T) {}

foo::<_>(iterable)

The exact details are explained in Do mutable references have move semantics?, but the general idea is, it is not documented. And the current working is such that when the compiler cannot determine without inference that something is a mutable reference, it is not reborrowed. Since _ requires inference, it is not reborrowed.

The receiver works differently (always reborrowed), and thus iterable.into_iter() does perform reborrowing. In fact, this behavior is related to autoref and not reborrowing. See also What are Rust's exact auto-dereferencing rules?.

CodePudding user response:

Besides using HIR on playground to inspect how for loop run behind the scene, Here is the official documentation on how for loop is de-sugared.

From the example given by the document, values (a vec type) was passed into IntoIterator::into_iter() IntoIterator::into_iter(values), which is different than calling as a method values.into_iter().

let values = vec![1, 2, 3, 4, 5];

for x in values {
    println!("{}", x);
}

De-sugared into

let values = vec![1, 2, 3, 4, 5];
{
    let result = match IntoIterator::into_iter(values) {
        mut iter => loop {
            let next;
            match iter.next() {
                Some(val) => next = val,
                None => break,
            };
            let x = next;
            let () = { println!("{}", x); };
        },
    };
    result
}
  • Related