DEV Community

BC
BC

Posted on

Day25:iter & into_iter & the confusion - 100DayOfRust

Yep, I came back to iter and into_iter again after 2 previous post. In one of the previous post I thought I already understood them: iter return referencer, into_iter return value, until I saw this:

fn main() {
    let v1 = vec![1,2,3];
    let v2 = [1,2,3];

    println!("{}", v1.into_iter().any(|x| x == 2));
    println!("{}", v2.into_iter().any(|x| *x == 2)); 
}
Enter fullscreen mode Exit fullscreen mode

Well I got why the closure for v1.into_iter use x directly, because into_iter return value, but why v2's into_iter returned a reference?

After spending time on searching it, I found the answer here in stackoverflow, the v2.into_iter actually is not calling into_iter on the array itself, it is applied to a reference to v2:

  1. IntoIterator is not implemented for [T; N], only for &[T; N] and &mut [T; N]
  2. When a method is not implemented for a value, it is automatically searched for references to that value instead

Also in the Rust doc here, it mentioned:

IntoIterator (implemented for &[T; N] and &mut [T; N])

Ok, so this v2.into_iter is actually (&v2).into_iter, if we change the code to:

println!("{}", (&v2).into_iter().any(|x| *x == 2)); 
Enter fullscreen mode Exit fullscreen mode

It works the same because Rust tries to find the into_iter on v2, but it cannot find it, so it searches this method on &v2, then it works. That's also why it yields a reference type as x in our closure.

So far so good, let's see another example: according to Rust's document: Rust's for loop syntax is actually sugar for iterators

So if we have code like this:

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

for x in values {
    println!("{}", x);
}
Enter fullscreen mode Exit fullscreen mode

It is the same as:

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

for x in v1.into_iter() {
    println!("{}", x);
}
Enter fullscreen mode Exit fullscreen mode

This works well, now let's try it on our array v2:

fn main() {
    let v1 = vec![1,2,3];
    let v2 = [1,2,3];


    for i in v1 {
        println!("{}", i);
    }

    for i in v2 {
        println!("{}", i);
    }
}
Enter fullscreen mode Exit fullscreen mode

This will give us a compile error!

10 |     for i in v2 {
   |              ^^ borrow the array with `&` or call `.iter()` on it to iterate over it
Enter fullscreen mode Exit fullscreen mode

Wait, if for i in v2 is the same as for i in v2.into_iter() and we know v2.into_iter() will be converted to (&v2).into_iter(), shouldn't this work? why this time Rust asks us to explicitly use &v2?

If we use code like this:

for i in &v2 {
    println!("{}", i);
}
Enter fullscreen mode Exit fullscreen mode

It works and makes sense, because it will change to for i in (&v2).into_iter()

or if we change code to this:

for i in v2.into_iter() {
    println!("{}", i);
}
Enter fullscreen mode Exit fullscreen mode

It would work too! Because now it found the into_iter method is on &v2, and actually calling (&v2).into_iter()

So for i in something is sometimes the same as for i in something.into_iter(), sometimes is not, which is so confusing.

Rust is known being strict on type and compiling, but this cause confusion when this code works:

println!("{}", v2.into_iter().any(|x| *x == 2)); 
Enter fullscreen mode Exit fullscreen mode

I'd rather Rust reported an error and force using &v2 like this:

println!("{}", (&v2).into_iter().any(|x| *x == 2)); 
Enter fullscreen mode Exit fullscreen mode

If it forces using in this way, then we would know for i in v2 won't work, we will write code for i in &v2 and we would know it equals to for i in (&v2).into_iter().

When v2 is an array, the way for i in v2 doesn't work, but for i in v2.into_iter() and for i in &v2 works, is just confusing.

Top comments (0)