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));
}
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:
- IntoIterator is not implemented for [T; N], only for &[T; N] and &mut [T; N]
- 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));
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);
}
It is the same as:
let v1 = vec![1, 2, 3];
for x in v1.into_iter() {
println!("{}", x);
}
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);
}
}
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
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);
}
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);
}
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));
I'd rather Rust reported an error and force using &v2
like this:
println!("{}", (&v2).into_iter().any(|x| *x == 2));
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)