Rust 1.53 IntoIterator

Hey,

I'm looking at the new 1.53 IntoIterator for arrays feature and I can't wrap my head around it:

fn main() {
    let vec = vec![10];
    for _ in vec.iter().chain([20]){
    }
}

This doesn't complie and the error message is not nice at all. I don't remember it to be so verbose.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=be232fba39739b9e22a793b1c289f440

Adding &[20] works (as in before 1.53) , but chain takes IntoIterator why doesn't it accept an array and I still have to & or .iter() ?

Also the above example is pretty much straight from the announcement page:

Am I missing something?

I thought we finally can do this, but we're not?

    for _ in [10,20].iter().chain([30,40]){
    }

I'd appreciate if anyone could explain in plain English please.

1 Like

You're creating a Vec<{integer}> in your topmost code example, not an array.
Furthermore, as indicated in the release notes, you'll have to use IntoIterator::into_iter(array) or for x in array {...} rather than for x in array.iter() {...} or for x in array.into_iter() {...} if you want to iterate over an array by value.

1 Like

Vec<i32>::iter() returns impl Iterator<Item=&i32>, so extend expects impl IntoIterator<Item=&i32>. Similarly, [i32; N]::iter() still returns returns impl Iterator<Item=&i32>, so extend expects impl IntoIterator<Item=&i32>.

Your original example will work with vec.iter().copied() to get Iterator<Item=i32>, and your second example will work with IntoIterator::into_iter([10, 20]) to get Iterator<Item=i32>.

Mixing up iterators of T and oterators of &T is a common mistake. What exactly was the error you ran into, and how might we adjust the wording that could've allowed to to realize what you miscommunicated to the compiler easier?

7 Likes

Here's the full error, as it stands on nightly:

error[E0271]: type mismatch resolving `<[{integer}; 1] as IntoIterator>::Item == &{integer}`
 --> src/main.rs:3:25
  |
3 |     for i in vec.iter().chain([20]) {}
  |                         ^^^^^ expected integer, found `&{integer}`

error[E0271]: type mismatch resolving `<std::array::IntoIter<{integer}, 1_usize> as Iterator>::Item == &{integer}`
 --> src/main.rs:3:14
  |
3 |     for i in vec.iter().chain([20]) {}
  |              ^^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
  |
  = note: required because of the requirements on the impl of `Iterator` for `std::iter::Chain<std::slice::Iter<'_, {integer}>, std::array::IntoIter<{integer}, 1_usize>>`
  = note: required because of the requirements on the impl of `IntoIterator` for `std::iter::Chain<std::slice::Iter<'_, {integer}>, std::array::IntoIter<{integer}, 1_usize>>`
  = note: required by `into_iter`

error[E0271]: type mismatch resolving `<std::array::IntoIter<{integer}, 1_usize> as Iterator>::Item == &{integer}`
 --> src/main.rs:3:14
  |
3 |     for i in vec.iter().chain([20]) {}
  |              ^^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
  |
  = note: required because of the requirements on the impl of `Iterator` for `std::iter::Chain<std::slice::Iter<'_, {integer}>, std::array::IntoIter<{integer}, 1_usize>>`
  = note: required by `std::iter::Iterator::next`

There's definitely room for improvement here. Ideally, I'd like to see something like this instead:

error[E0271]: type mismatch resolving `<[{integer}; 1] as IntoIterator>::Item == &{integer}`
 --> src/main.rs:3:25
  |
3 |     for i in vec.iter().chain([20]) {}
  |                         ^^^^^ expected integer, found `&{integer}`
  = help: try iterating by reference instead
        for i in vec.iter().chain(&[20]) {}
                                  ^
  = help: try copying the iterated items
        for i in vec.iter().copied().chain([20]) {}
                           ^^^^^^^^^
  = help: try iterating by value instead
        for i in vec.into_iter().chain([20]) {}
                    ^^^^^^^^^^^^

The third suggestion is probably the hardest, but the first two should actually be somewhat reasonable. The other two errors are just rehashing the existing problem, in that the result of calling Chain is malformed, so ideally would be suppressed.

(I won't be able to open an issue for this for a while, so if someone else can, please do!)

2 Likes

The error is just that the first iterator is an iterator over references to elements and the second over element values.

You can chain together iterators that have the same type of iterator item, like this:

IntoIterator::into_iter([10, 20]).chain([30,40])
5 Likes

Got it. Carefully reading the release notes, made it clear. Currently array.into_iter() and array.iter() are both resolve to &Item iterator for backward compatibility, and in the upcoming edition this will likely be changed so IntoIterator::into_iter(array) and array.into_iter() are equal.

Thanks all!

2 Likes

Nitpick: the current, forward-compatible way to create an iterator of T from a [T; N] is std::array::IntoIter::new(array), not IntoIterator::into_iter(array). [ignore this, sorry]

Why the nitpick? Either way will work, and will continue to work in the future. You can even infer it with <_>::into_iter(array) if you want a little more brevity.

The only thing affected by the future is the method call syntax, array.into_iter().

1 Like

Rustc switched from IntoIter::new to IntoIterator::into_iter, so I'd think the latter is preferred. Generally, IntoIter types shouldn't need to be referred to by name.

Using IntoIter directly was only necessary for the small number of releases before arrays were IntoIterator.

Which you use is a style preference for now. And in edition=2021+, .into_iter() will do the correct thing.

3 Likes

FYI, submitted an issue for improving the error message:

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.