For loops in Rust

I've been fighting with rust a bit when it comes to for loops, so I decided to dig into the issues. I made a blog post about it: Rust for loops. It seems to me that when a feature as fundamental as for loops has so much detail, it might be worth unpacking that in the Rust Book.

5 Likes

Nice post! The second iteration of the book (here) will have a whole section on iterators, and their relationship with for will be discussed heavily.

2 Likes

@willu: As someone new to rust, this is very helpful! Thanks for writing it.

@steveklabnik Also, I'm excited by what I see in the second revision of the book :slight_smile:

There has been some discussion over in the Reddit thread about dereferencing. I came up with this example that I think shows a few interesting things. Naively all of the following implementations should be similar, yet you can see that some fail and others do not. Some of these have good reasons for failing (e.g. you can't call a method that takes self on a reference), but others could work if the standard library had a little more code.

#[allow(unused_variables)]
fn main() {
  let r = 1..5;
  let v = vec![1, 2, 3, 4];
  let mut iter = IntoIterator::into_iter(r);  // Works.
  // let mut iter = IntoIterator::into_iter(v);  // Works.
  // let mut iter = IntoIterator::into_iter(&r);  // Fails: trait `&std::ops::Range<{integer}>: std::iter::Iterator` not satisfied.
  // let mut iter = IntoIterator::into_iter(&v);  // Works.
  // let mut iter = IntoIterator::into_iter(&&v);  // Fails: trait `&&std::vec::Vec<{integer}>: std::iter::Iterator` not satisfied.
  // let mut iter = r.into_iter();  // Works.
  // let mut iter = v.into_iter();  // Works.
  // let mut iter = (&r).into_iter();  // Fails: cannot move out of borrowed content.
  // let mut iter = (&v).into_iter();  // Works.
  // let mut iter = (&&v).into_iter();  // Works.
  // let mut iter = r.iter();  // Fails: no method named `iter` found for type `std::ops::Range<{integer}>` in the current scope.
  // let mut iter = v.iter();  // Works.
  // let mut iter = (&v).iter();  // Works.
  // let mut iter = (&&v).iter();  // Works.
  loop {
    match iter.next() {
      Some(x) => {
        println!("{}", x);
      },
      None => break,
    }
  }
}

You can see that auto dereferencing doesn't apply when you use the explicit IntoIterator::into_iter() form. You can also see that there is no implementation of IntoIterator for references to ranges, unlike references to vectors.

On that note, there's no IntoIterator for standard Ranges, either. Well, I mean, there is, but not like you might be thinking...

    let mut iter = r.into_iter().into_iter();  // Works.
    let mut iter = r.into_iter();  // Works.
    let mut iter = r;  // Works. ;)

Er, yeah. :slight_smile: