据我了解,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
在语义上有什么错误吗?出于某种原因将它用于与文档所述用途不同的目的是个坏主意吗?另外,有没有更好的方法来实现同样的事情?
最佳答案
首先,让我们澄清一下您问题中的这句旁白:
For example, (1..10) could implement this to be able to go from 1 to 10, or from 10 to 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很清楚:
A range iterator able to yield elements from both ends
A
DoubleEndedIterator
can be thought of as a deque in thatnext()
andnext_back()
exhaust elements from the same range, and do not work independently of each other.
你必须理解为:无论是否使用next
, next_back
,或它们的组合,如果您跟踪它们(并跟踪它们来自哪一端),则任何遍历都必须产生相同范围的元素。
但是,存在无限范围的情况......
在无限长的范围内,终点永远不会相遇。这种迭代器的一个例子是 repeat
.对于这个简单的例子,很容易看出为什么迭代器是无限的,以及 next()
是如何无限的。和 next_back()
逻辑上定义。
所以这是一个漏洞,您可以将迭代器的序列指定为无限长,尽管是双端的。我认为尝试使用我们的固定位宽整数正确实现此接口(interface)是有问题的。
即使有可疑的哲学动机,它也可能是一个非常令人困惑的迭代器,违背了合理的期望。我认为错误地使用 trait 会很糟糕(比如迭代器有两个完全不同的末端),但是如果范围是无穷无尽的,你实际上不会破坏任何 trait 属性。
关于rust - 有什么理由不对不迭代一系列事物的迭代器使用 DoubleEndedIterator 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31818329/