Reputation: 93
I'm new to Rest and learning it from "Rust Essential Training" on Linkedin Learning. At the end of chapter 5 (Program Flow and Control), I got the following question as a chapter quiz.
What's wrong with the following code to print each element of
my_array
?fn main() { let my_array = [1, 2, 3]; for element in my_array { println!("element is {}", element); } }
I couldn't think of anything wrong with it, and when I ran it on visual code, it did run without any problem, yet they said
The for loop cannot iterate over
my_array
directly.This program needs to use
my_array.iter()
to get an iterator over the array.
Can someone give me a meaningful explanation? Am I missing something here?
Upvotes: 4
Views: 177
Reputation: 155383
It actually works just fine on the newest versions of Rust (1.53 and higher). You can see for yourself on the Rust playground.
Prior to 1.53, this didn't work, because arrays themselves did not implement IntoIterator
. As of Rust 1.53, somewhat confusingly, all editions of Rust actually implement IntoIterator
for arrays, but:
.into_iter()
on an array is equivalent to direct iteration (it consumes).into_iter()
is equivalent to calling .iter()
(non-consuming iteration by reference) or more strictly, array.into_iter()
resolves to (&array).into_iter()
, and converting the array reference to an iterator makes a non-consuming iterator backed by the array.The distinction between editions is due to a "small hack" that makes it so, even though arrays implement IntoIterator
on all editions as of 1.53, on 2015 & 2018, the compiler hides this fact solely for the case of .into_iter()
method call syntax on arrays, so it instead resolves to (&array).into_iter()
(which is what happened back when arrays did not implement IntoIterator
at all). It's a hack, because it defends against method call syntax alone; any other means of iterating the array (e.g. direct iteration, iter.zip
, directly invoking IntoIterator::into_iter
on the array) will find it's an IntoIterator
and iterate it by (consuming) value, but none of those methods worked prior to 1.53 anyway, so no existing code was relying on them (compiling array.into_iter()
on Rust 2015 or 2018 will emit a loud warning that the meaning changes in Rust 2021).
Prior to 1.53 you had to be explicitly convert to an array reference/slice (which do support iteration), or explicitly call .iter()
or .into_iter()
on the array (the latter of which is still non-consuming, resolving to (&array).into_iter()
; this is why the hack was needed, so the new feature didn't change the behavior in existing code for 2015 & 2018).
Mind you, in this particular case, the elements of your array implement Copy
, so the array contents are not consumed no matter how it's iterated; the only distinction is whether you're iterating over references to integer or over integer values. But for non-Copy
types, the distinction between consuming the actual values and merely "viewing" by reference is important.
Upvotes: 6