Deep dive into the Rust `for` loop.

Consider the following Rust code:

let myList = vec![1, 2, 3];

for item in myList {
    println!("{}",  item );
}

Output:
1
2
3

This creates a vector and iterates over the values. Simple enough.

Now, let's look at this after removing the abstraction offered by the for loop.

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

In order to understand the above code, let's go over some of the elements involved.

The Iterator trait

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

There are many more fields in this trait, but here I have only included the required ones.

If you're not sure what an Option is, just think of it as representing either None or some value of the type enclosed within the <>, which is Item in this case. So, this would wither return None or Some(Item).

next() defines what the next item looks like and Item is an associated type meaning it can derive its type based on which object is representing it.

So, for a hashmap, the type can be a key-value pair, whereas for an array it can be the type of the array element.

The for loop internally utilizes this next() function in each iteration to get the next element. So, for a Vector, the next function would just update the current index to the index of the next item.

IntoIterator trait

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

By implementing IntoIterator for a type, you define how it will be converted to an iterator. This is already defined for collection types like arrays, vectors and hashmaps.

Going back to our original for code

// Defining a vector collection object.
let myList = vec![1, 2, 3];
{
    // IntoIterator::into_iter(myList) calls the into_iter() method 
    // of the vector object. (This is already implemented by the vector)
    // The result of this is an iterator object.
    let result = match IntoIterator::into_iter(myList) {

        // The itorator object returned by the into_iter() is matched here.
        mut iter => loop {
            let next;

            // The next() function of the iterator is 
            // used to fetch the next element of the collection. 
            match iter.next() {
                Some(val) => next = val,
                // If a None value is returned, the loop ends.
                None => break,
            };

            // If the element returned by next() isn't None
            // it is printed. (this println! represents the body of the for loop).
            let item  = next;
            let () = { println!("{}", item ); };
        },
    };
    result
}

I have added comments inline for better understanding.