Decomposing a for-each loop

I would like to kindly share with you how one can think of the for loop described in The book section Methods for Iterating Over Strings. And ask you if this is correct or something else is happening.

The for loop is this:

fn main() {
    let s: String = String::from("नमस्ते");
    
    for c in s.chars() {
        println!("{}", c);
    }
}

This simple for loop is actually what the following code is doing:

fn main() {
    let s: String = String::from("नमस्ते");

    let mut i = s.chars(); // Returns an iterator

    let elem: Option<char> = i.next(); // Advances the iterator and returns the next value.
    if let Some(c) = elem {
        // take the `char` value out of the Option
        println!("{}", c);
    }

    let elem: Option<char> = i.next();
    if let Some(c) = elem {
        println!("{}", c);
    }

    let elem: Option<char> = i.next();
    if let Some(c) = elem {
        println!("{}", c);
    }

    let elem: Option<char> = i.next();
    if let Some(c) = elem {
        println!("{}", c);
    }

    let elem: Option<char> = i.next();
    if let Some(c) = elem {
        println!("{}", c);
    }

    let elem: Option<char> = i.next();
    if let Some(c) = elem {
        println!("{}", c);
    }

    // the iterator is ended at this point
    // every time you call `next` from here on you get `None`
    let elem: Option<char> = i.next();
    if let None = elem {
        println!("none...");
    }

    let elem: Option<char> = i.next();
    if let None = elem {
        println!("none...");
    }
}

The above code of course is not good, so you can impove it with a loop:

fn main() {
    let s: String = String::from("नमस्ते");

    let mut i = s.chars();

    loop {
        let elem: Option<char> = i.next();

        if let Some(c) = elem {
            println!("{}", c);
        } else {
            break;
        }
    }
}

Or use the match expression:

fn main() {
    let s: String = String::from("नमस्ते");

    let mut i = s.chars();

    loop {
        let elem: Option<char> = i.next();

        match elem {
            Some(c) => println!("{}", c),
            None => break,
        }
    }
}

Of course we must use the more ergonomic and concise first version of the code:

fn main() {
    let s: String = String::from("नमस्ते");

    for c in s.chars() {
        println!("{}", c);
    }
}

You can use the following command, to get bare-bones unsugared Rust code:

cargo +nightly rustc -- -Zunpretty=hir

Example

use ::core::mem::drop as do_stuff; // avoid bloat from expanding actual println!

fn main() {
    let s: String = String::from("नमस्ते");
    
    for c in s.chars() {
        do_stuff(c);
    }
}

gives, with cargo +nightly rustc -- -Zunpretty=hir | rustfmt --edition=2018

#[prelude_import]
use ::std::prelude::v1::*;
#[macro_use]
extern crate std as std;
use ::core::mem::drop as do_stuff;

fn main() {
    let s: String = <String>::from("नमस्ते");

    {
        let _result = match ::std::iter::IntoIterator::into_iter(s.chars()) {
            mut iter => loop {
                let mut __next;
                match ::std::iter::Iterator::next(&mut iter) {
                    ::std::option::Option::Some(val) => __next = val,
                    ::std::option::Option::None => break,
                }
                let c = __next;
                {
                    do_stuff(c);
                }
            },
        };
        _result
    }
}
7 Likes

There's yet another way to write it:

fn main() {
    let s: String = String::from("नमस्ते");
    let mut iter = s.chars();

    while let Some(c) = iter.next() {
        println!("{}", c)
    }
}

This is the simplest way to de-sugar a for-loop.

1 Like

The use of IntoIterator shown in the expansion by @Yandros is an important part. It's a no-op for things which are already iterators, like s.chars(), but it is necessary for bare collections, like for x in vec.

3 Likes

This is documented in the std::iter docs.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.