IntoIterator for arrays
Summary
- Arrays implement
IntoIterator
in all editions. - Calls to
IntoIterator::into_iter
are hidden in Rust 2015 and Rust 2018 when using method call syntax (i.e.,array.into_iter()
). So,array.into_iter()
still resolves to(&array).into_iter()
as it has before. array.into_iter()
changes meaning to be the call toIntoIterator::into_iter
in Rust 2021.
Details
Until Rust 1.53, only references to arrays implement IntoIterator
.
This means you can iterate over &[1, 2, 3]
and &mut [1, 2, 3]
,
but not over [1, 2, 3]
directly.
for &e in &[1, 2, 3] {} // Ok :)
for e in [1, 2, 3] {} // Error :(
This has been a long-standing issue, but the solution is not as simple as it seems.
Just adding the trait implementation would break existing code.
array.into_iter()
already compiles today because that implicitly calls
(&array).into_iter()
due to how method call syntax works.
Adding the trait implementation would change the meaning.
Usually this type of breakage (adding a trait implementation) is categorized as 'minor' and acceptable. But in this case there is too much code that would be broken by it.
It has been suggested many times to "only implement IntoIterator
for arrays in Rust 2021".
However, this is simply not possible.
You can't have a trait implementation exist in one edition and not in another,
since editions can be mixed.
Instead, the trait implementation was added in all editions (starting in Rust 1.53.0)
but with a small hack to avoid breakage until Rust 2021.
In Rust 2015 and 2018 code, the compiler will still resolve array.into_iter()
to (&array).into_iter()
like before, as if the trait implementation does not exist.
This only applies to the .into_iter()
method call syntax.
It does not affect any other syntax such as for e in [1, 2, 3]
, iter.zip([1, 2, 3])
or
IntoIterator::into_iter([1, 2, 3])
.
Those will start to work in all editions.
While it's a shame that this required a small hack to avoid breakage, this solution keeps the difference between the editions to an absolute minimum.
Migration
A lint, array_into_iter
, gets triggered whenever there is some call to into_iter()
that will change
meaning in Rust 2021. The array_into_iter
lint has already been a warning by default on all editions
since the 1.41 release (with several enhancements made in 1.55). If your code is already warning free,
then it should already be ready to go for Rust 2021!
You can automatically migrate your code to be Rust 2021 Edition compatible or ensure it is already compatible by running:
cargo fix --edition
Because the difference between editions is small, the migration to Rust 2021 is fairly straight-forward.
For method calls of into_iter
on arrays, the elements being implemented will change from references to owned values.
For example:
fn main() { let array = [1u8, 2, 3]; for x in array.into_iter() { // x is a `&u8` in Rust 2015 and Rust 2018 // x is a `u8` in Rust 2021 } }
The most straightforward way to migrate in Rust 2021, is by keeping the exact behavior from previous editions
by calling iter()
which also iterates over owned arrays by reference:
fn main() { let array = [1u8, 2, 3]; for x in array.iter() { // <- This line changed // x is a `&u8` in all editions } }
Optional migration
If you are using fully qualified method syntax (i.e., IntoIterator::into_iter(array)
) in a previous edition,
this can be upgraded to method call syntax (i.e., array.into_iter()
).