Why into_iter() can be called on [T;N]?

From the doc it reads:

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

Arrays coerce to slices ([T]), so a slice method may be called on an array.

fn main() {
    let arr = [1,2,3,4];
    
    for v in arr.into_iter() {
        println!("{}", v);
    }
}

Initially I am thinking arr is coerced to [T] and calling into_iter() on [T]. However, per documentation, IntoIterator is only implemented for &'a [T] and &'a mut [T] , there is no way into_iter() was called on [T].

So what's going on here?

I think it's just syntactic sugar, that the compiler changes to

fn main() {
    let arr = [1,2,3,4];
    
    for v in &arr.iter() {
        println!("{}", v);
    }
}

This issue might be related
https://github.com/rust-lang/rust/issues/25725

Have a look rust#25725.

And you can try:

let a = [1, 2, 3];
for i in a.into_iter() {
    println!("{}", i);
}

println!("{:?}", a);

It doesn't take the ownership of a.

It should be for v in &arr or for v in arr.iter().

But If the compiler implicitly change into_iter() to iter(), then it violate how the docs describes:

There are three common methods which can create iterators from a collection:

iter(), which iterates over &T.
iter_mut(), which iterates over &mut T.
into_iter(), which iterates over T.

Just make more sense to reject the code.

The last part of the answer

The author of the Rust by Example post illustrates the surprise coming from the dependence on the context (i.e., the type) on which into_iter is called, and is also compounding the problem by using the fact that:

  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

which is very surprising for into_iter since all types (except [T; N] ) implement it for all 3 variations (value and references). It's not possible for the array to implement an iterator that yields values because it cannot "shrink" to give up its items

So yes, it doesn't implicitly change it to

for _ in &arr.iter()

and instead changes it to

for _ in &arr.into_iter()

You should also avoid calling .into_iter() on an array because the standard library is allowed to introduce impl<T> IntoIterator for [T; _] in the future which will change the behaviour of this code (and the more code in the wild using this the harder it will be to introduce).

Luckily clippy has a lint denying this usage, that will hopefully be ported into rustc at some point:

error: this .into_iter() call is equivalent to .iter() and will not move the array
 --> src/main.rs:4:18
  |
4 |     for v in arr.into_iter() {
  |                  ^^^^^^^^^ help: call directly: `iter`
  |
  = note: `#[deny(clippy::into_iter_on_array)]` on by default

(This can get even more complicated as &[T; 257] doesn't implement IntoIterator yet the example code also works for it. There's another step to method resolution which includes unsized coercions and finds the slice implementation impl<T> IntoIterator for &[T] and chooses to use that. Interestingly that one is just a warning in clippy rather than an error.)

6 Likes

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