是否有任何理由不将DoubleEndedIterator用于不迭代一系列事物的迭代器?

时间:2015-08-04 20:05:46

标签: rust

据我了解,DoubleEndedIterator适用于迭代一系列项目并可从任一端开始的迭代器。例如,(1..10)可以实现这一点,从1到10,或从10到1。但是,我有一种情况,将此特征用于不迭代的迭代器是有用的在一系列事物上,但可以从同一个位置前进或后退,如下:

struct ExtremeIterator {
    number: i32
}

impl Iterator for ExtremeIterator {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        let result = self.number;
        self.number += 1;
        Some(result)
    }
}

impl DoubleEndedIterator for ExtremeIterator {
    fn next_back(&mut self) -> Option<i32> {
        let result = self.number;
        self.number -= 1;
        Some(result)
    }
}

fn main() {
    let iter = ExtremeIterator { number: 10 };

    for i in iter.rev().take(5) {
        println!("{}", i);
    }
}

所以这是我的问题:使用DoubleEndedIterator这种方式有什么语义错误吗?是否有某种原因使用它的目的与文档所说的不同?还有,有更好的方法来实现同样的目标吗?

1 个答案:

答案 0 :(得分:9)

首先,让我们在你的问题中澄清这句话:

  

例如,(1..10)可以实现此功能,可以从1到10,或从10到1。

1..10的类型为std::ops::Range<T>,无论您是否迭代,它都会迭代到 1,2,3,4,5,6,7,8,9 <序列从后面或前面。从后面开始,你会得到 9,8,... 1,2,... ;它是一个开始包容,最终排他范围,也称为半开范围。

如果你要从前面然后从后面迭代一下这个范围,那么它会在两端相遇的地方停止:

let mut range = 1..10;

for i in &mut range {
    // Produce 1, 2, 3, 4, 5
    // then break.
    if i == 5 { break; }
}

for j in range.rev() {
    // Produces 9, 8, 7, 6
}

这表明一个表现良好的双端迭代器是什么样的。 (rust playpen link)

现在,关于DoubleEndedIterator“虐待”的问题:

这是有问题的。

DoubleEndedIterator documentation很清楚:

  

能够从两端产生元素的范围迭代器

     

DoubleEndedIterator可以被认为是来自同一范围的next()next_back() 排气元素中的双端队列,并且不会彼此独立地工作。

您必须将其理解为:无论使用nextnext_back还是其中的组合,任何遍历都必须生成相同范围的元素(如果您跟踪它们)(并跟踪哪些元素)结束他们来自。)

然而,存在无限范围的情况......

在无限长的范围内,两端永远不会相遇。这种迭代器的一个例子是repeat。对于这个简单的例子,很容易理解为什么迭代器是无限的,以及next()next_back()如何在逻辑上定义。

所以这是一个漏洞,您可以将迭代器的序列指定为无限长,尽管双重结束。我认为用我们的固定位宽整数来正确地实现这个接口是值得怀疑的。

即使有可疑的哲学动机,它也可能是一个非常混乱的迭代器,它无视合理的期望。我认为使用特征错误(比如说迭代器有两个完全不同意的结果)会很糟糕,但是在取之不尽的范围内,你实际上并没有打破任何特质属性。