【问题标题】:Why does the Fuse iterator adapter not work as expected?为什么 Fuse 迭代器适配器不能按预期工作?
【发布时间】:2017-05-12 22:07:51
【问题描述】:

我正在尝试使用 Fuse 迭代器适配器并且得到了意想不到的结果 (Playground link):

fn main() {
    let mut i1 = (1..3).scan(1, |_, x| {
        if x < 2 { None } else { Some(x) }
    });
    println!("{:?}", i1.next());
    println!("{:?}", i1.next());
    println!("{:?}", i1.next());
    println!("");

    let mut i2 = (1..3).scan(1, |_, x| {
        if x < 2 { None } else { Some(x) }
    }).fuse();
    println!("{:?}", i2.next());
    println!("{:?}", i2.next()); // This should print None
    println!("{:?}", i2.next());
    println!("");
}

哪些打印:

None
Some(2)
None

None
Some(2)
None

迭代器i1 正在返回我所期望的。它返回None,然后是Some(2),然后是Nonei2 是与 fuse() 适配的相同迭代器。 Fuse 应该让它在第一个None 之后返回None,并且由于它返回的第一个值是None,所以它应该是它返回的唯一值。但是,它的行为与i1 相同。我做错了什么?

【问题讨论】:

    标签: iterator rust


    【解决方案1】:

    TL;DR 摘要:此was a bug 已在 Rust 1.19 及更高版本中修复。

    我很确定你没有做错任何事。这似乎是一个错误(我的猜测)或 非常 令人困惑的交互。看看这个扩展的例子:

    #![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()); // All `None`s
        dump("Fuse<Box<FusedIterator>>", boxed_no_fuse_but_fused().fuse());
    }
    

    诀窍在于FusedIterator 是一个旨在提高效率的不稳定特征。它让Iterator::fuse 知道这是一个空操作。

    但是,在这种情况下,条件是必要的,但不是充分的:

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

    确实,如果底层迭代器FusedIterator并开始返回Nonescan将继续返回None。然而,这不是获得None 的唯一方法——闭包 也可以返回None

    【讨论】:

    • 我已经打开issue 41964,让我们看看会发生什么。
    • 我认为这一定是一个错误,但不知道这种特质专业化魔法,所以一直在查看 Fuse 本身的代码,看起来不错。有趣的是,我问过的唯一一个 Rust 问题也是一个错误发现。
    • @wingedsubmariner you've asked two others,在 Rust 1.0 之前计算错误实际上是在作弊 ^_^
    • 啊,你是对的,不知道我怎么没找到那个。 ^_^
    • 总结:这是一个错误,已在 Rust 1.19 及更新版本中修复。
    猜你喜欢
    • 1970-01-01
    • 2021-05-09
    • 2017-06-08
    • 2015-07-10
    • 1970-01-01
    • 2021-05-30
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多