Open
Description
I tried this code:
pub fn using_each() {
let mut it = 0..4;
it.by_ref().zip(0..2).for_each(|_| {});
println!("{:?}", it);
}
pub fn using_loop() {
let mut it = 0..4;
for _ in it.by_ref().zip(0..2) {}
println!("{:?}", it);
}
pub fn using_each_dyn() {
let mut it = 0..4;
(&mut it as &mut dyn Iterator<Item = i32>).zip(0..2).for_each(|_| {});
println!("{:?}", it);
}
pub fn using_loop_dyn() {
let mut it = 0..4;
for _ in (&mut it as &mut dyn Iterator<Item = i32>).zip(0..2) {}
println!("{:?}", it);
}
fn main() {
using_each();
using_loop();
using_each_dyn();
using_loop_dyn();
}
I expected all four functions to behave identically, but instead the above code outputs:
2..4
3..4
3..4
3..4
This occurs because:
- In the general case, the
Zip
iterator will call.next()
on the first iterator to check if the value isNone
, and then call.next()
on the second iterator. This means that if the second iterator is shorter, then the first iterator will have.next()
called one past the length of the second iterator. - In the
TrustedLen
specialization ofZip
'sfold()
method, if the second iterator is shorter, then both iterators have their.next()
called exactly equal to that second iterator's length.
rust/library/core/src/iter/adapters/zip.rs
Lines 666 to 695 in 231257f
This mismatch in behavior seems undesirable.
Discovered in #143966.
Meta
Reproducible on the playground with version 1.90.0-nightly (2025-07-15 3014e79f9c8d5510ea7b)
@rustbot labels +A-iterators +A-specialization