就像你已经做的那样,让我们来看一下它的源代码,这会揭示一些内部工作原理:
pub struct Peekable<I: Iterator> {
iter: I,
peeked: Option<Option<I::Item>>,
}
连同其 next()
实现一起:
impl<I: Iterator> Iterator for Peekable<I> {
fn next(&mut self) -> Option<I::Item> {
match self.peeked.take() {
Some(v) => v,
None => self.iter.next(),
}
}
}
以及它的peek()
实现:
impl<I: Iterator> Peekable<I> {
pub fn peek(&mut self) -> Option<&I::Item> {
let iter = &mut self.iter;
self.peeked.get_or_insert_with(|| iter.next()).as_ref()
}
}
Peek 包装 一个现有的迭代器。现有的迭代器是不可预览的。
所以,peek 的作用是:
- 在
peek()
上:
- 从包装的迭代器中获取
next()
项并将其存储在 self.peeked
中(如果 self.peeked
尚未包含下一项)
- 返回对预览项的引用
- 在
next()
上:
- 查看我们当前是否有
self.peeked
项
- 如果有,则返回该项
- 如果没有,则从底层迭代器中获取
next()
项。
因此,正如您已经意识到的那样,peek()
操作需要 &mut self
,因为它可能需要通过调用底层迭代器上的 next()
来生成下一个预览项。
因此,如果您从更抽象的角度来看待它,则存在以下原因:下一个项可能尚不存在。因此,“窥视”可能涉及实际生成下一个项,这绝对是对基础迭代器的突变操作。
并非所有的迭代器都在数组/切片上,其中项已经存在;迭代器可能是任何生成多个项的东西,包括仅在要求该项时才创建该项的惰性发生器。
他们是否可以以不同的方式实现它?
是的,他们完全有可能以不同的方式实现。他们本来可以在new()期间对基础迭代器进行next()操作。然后,当某人在Peekable上调用next()时,它可以返回当前查看的值并立即查询下一个值。然后,窥视将成为一个&self方法。
为什么他们那样做还不清楚,但最肯定是为了使迭代器尽可能地懒惰。在大多数情况下,惰性迭代器是一件好事。
话虽如此,这里有一个概念验证,展示了如何实现一种预取可查看迭代器,而不需要对peek()
使用&mut
:
pub struct PrefetchingPeekingIterator<I: Iterator> {
iter: I,
next_item: Option<I::Item>,
}
impl<I: Iterator> PrefetchingPeekingIterator<I> {
fn new(mut iter: I) -> Self {
let next_item = iter.next();
Self { iter, next_item }
}
fn peek(&self) -> Option<&I::Item> {
self.next_item.as_ref()
}
}
impl<I: Iterator> Iterator for PrefetchingPeekingIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
std::mem::replace(&mut self.next_item, self.iter.next())
}
}
fn main() {
let mut range = PrefetchingPeekingIterator::new(1..10);
dbg!(range.next().unwrap());
dbg!(range.peek().unwrap());
dbg!(range.next().unwrap());
dbg!(range.peek().unwrap());
dbg!(range.next().unwrap());
dbg!(range.peek().unwrap());
}
[src/main.rs:27] range.next().unwrap() = 1
[src/main.rs:28] range.peek().unwrap() = 2
[src/main.rs:29] range.next().unwrap() = 2
[src/main.rs:30] range.peek().unwrap() = 3
[src/main.rs:31] range.next().unwrap() = 3
[src/main.rs:32] range.peek().unwrap() = 4
next()
方法中的take()
调用是移除了预览的值,这样当我们再次调用next()
时就会得到一个新的值。 - anilbey&mut
。也许这可以满足您的使用需求。 - Finomnispeak()
首先会调用next()
,这将删除峰值并使用get_or_insert_with
重新插入相同的值,对吗? - anilbeyget_or_insert_with()
只有在没有值的情况下才会调用next()
。请注意,它前面有||
,使其成为一个闭包。 - Finomnispeek
的非变异性,他们也可以使用内部变异,但是如果他们使用RefCell
,它就不会是Sync
,如果他们使用Mutex
,即使不需要,你也会付出额外的性能代价。 - rodrigo