Hello,
According to the Rust Reference a for
loop
'label: for PATTERN in iter_expr {
/* loop body */
}
is equivalent to
{
let result = match IntoIterator::into_iter(iter_expr) {
mut iter => 'label: loop {
let mut next;
match Iterator::next(&mut iter) {
Option::Some(val) => next = val,
Option::None => break,
};
let PATTERN = next;
let () = { /* loop body */ };
},
};
result
}
Why is the single-arm match expression above needed? Isn't the for loop already equivalent to the following shorter template?
{
let mut iter = IntoIterator::into_iter(iter_expr);
'label: loop {
let mut next;
match Iterator::next(&mut iter) {
Option::Some(val) => next = val,
Option::None => break,
};
let text = next;
let () = { /* loop body */ };
}
}
Actually, I got into the above while trying to understand why a for loop over v.iter()
and &v
seems to behave the same (v
is some Vec<T>
or &Vec<T>
). The answer seems to be that this is because the stdlib implements the IntoIterator
trait for references to vectors. In other words, a user-defined collection type would not necessarily behave in the same way.
Now that it is clear that for
iterates over the result of IntoIterator::into_iter(iter_expr)
, shouldn't deref conversion apply? For example, when v
is a Box<Vec<T>>
, v.iter()
will work, but &v
won't (but &*v
will...). Concretely, in the following working program &*v
may be replaced by v.ter()
but replacing it by &v
is an error.
fn main() {
let v = ["apples", "cake", "coffee"];
let v = v.map(|s| String::from(s));
let v: Box<[String]> = Box::from(v);
for text in &*v {
println!("I like {}.", text);
}
}
But shouldn't &v
be allowed because of deref conversion? In that RFC the following example is given:
fn use_nested(t: &Box<T>) {
use_ref(&**t); // what you have to write today
use_ref(t); // what you'd be able to write (note: recursive deref)
}
In the above, substitute &v
for t
to seemingly arrive at the same situation as my example.
Possible solution: In the following variant, deref conversion does indeed work:
fn my_iter(v: &[String]) -> std::slice::Iter<String> {
IntoIterator::into_iter(v)
}
fn main() {
let v = ["apples", "cake", "coffee"];
let v = v.map(|s| String::from(s));
let v: Box<[String]> = Box::from(v);
for text in my_iter(&v) {
println!("I like {}.", text);
}
}
So it seems that calling a trait function like IntoIterator::into_iter
does not quite behave in the same way as calling a simple function like my_iter
. I would be interested to better understand the background of this.