Skip to content

iter::Scan should not unconditionally implement FusedIteratorΒ #41964

Closed
@shepmaster

Description

@shepmaster

Scan unconditionally implements FusedIterator if the wrapped iterator does:

impl<B, I, St, F> FusedIterator for Scan<I, St, F>
    where I: FusedIterator, F: FnMut(&mut St, I::Item) -> Option<B> {}

Since the closure to scan can choose to return None, this implementation is overly broad. It would need to take the closure into account, which seems... not possible.

Here's a reproduction showing the unexpected behavior when the closure returns None:

#![feature(fused)]

fn dump<I: Iterator<Item = i32>>(label: &str, mut iter: I) {
    println!("= Running: {}", label);
    for _ in 0..10 {
        println!("{:?}", iter.next());
    }
    println!("");
}

fn boxed_internal_fuse() -> Box<Iterator<Item = i32>> {
    Box::new((1..3)
        .scan(1, |_, x| if x < 2 { None } else { Some(x) })
        .fuse())
}

fn boxed_no_fuse() -> Box<Iterator<Item = i32>> {
    Box::new((1..3)
        .scan(1, |_, x| if x < 2 { None } else { Some(x) }))
}

use std::iter::FusedIterator;
fn boxed_no_fuse_but_fused() -> Box<FusedIterator<Item = i32>> {
    Box::new((1..3)
        .scan(1, |_, x| if x < 2 { None } else { Some(x) }))
}

fn main() {
    let i1 = (1..3)
        .scan(1, |_, x| if x < 2 { None } else { Some(x) });
    dump("Scan", i1);
    
    let i2 = (1..3)
        .scan(1, |_, x| if x < 2 { None } else { Some(x) })
        .fuse();
    dump("Fuse<Scan>", i2);
    
    dump("Box<Fuse<Scan>>", boxed_internal_fuse());
    dump("Fuse<Box<Iterator>>", boxed_no_fuse().fuse());
    dump("Fuse<Box<FusedIterator>>", boxed_no_fuse_but_fused().fuse());
}
= Running: Scan
None
Some(2)
None
None
None
None
None
None
None
None

= Running: Fuse<Scan>
None
Some(2)
None
None
None
None
None
None
None
None

= Running: Box<Fuse<Scan>>
None
Some(2)
None
None
None
None
None
None
None
None

= Running: Fuse<Box<Iterator>>
None
None
None
None
None
None
None
None
None
None

= Running: Fuse<Box<FusedIterator>>
None
Some(2)
None
None
None
None
None
None
None
None

This example requires nightly for the feature to demonstrate, but the effect can be felt in stable Rust.

Originally reported via Stack Overflow

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions