我正试图用group_by_count
方法扩展Iterator:
use itertools::Itertools;
trait ExtIterator<T>
where
T: Sized,
{
fn group_by_count(self) -> Box<dyn Iterator<Item = (T, usize)>>
where
T: Sized;
}
impl<T: 'static, I: 'static> ExtIterator<T> for I
where
I: Iterator<Item = T> + Sized,
T: Clone + Eq + PartialEq + Sized,
{
fn group_by_count(self) -> Box<dyn Iterator<Item = (T, usize)>>
where
Self: Sized,
{
Box::new(
self.group_by(|i| i.clone())
.into_iter()
.map(|(key, group)| (key, group.count())),
)
}
}
我得到:
error[E0515]: cannot return value referencing temporary value
--> src/ext/iterator.rs:21:9
|
21 | / Box::new(
22 | | self.group_by(|i| i.clone())
| | ---------------------------- temporary value created here
23 | | .into_iter()
24 | | .map(|(key, group)| (key, group.count())),
25 | | )
| |_________^ returns a value referencing data owned by the current function
|
= help: use `.collect()` to allocate the iterator
在这里调用collect
感觉不对,clippy建议如果我这样做,应该删除collect
。但我不知道如何避免创建临时值。
如果没有自定义迭代器适配器,就无法做到这一点。
但是,创建一个新的GroupByCount
迭代器很容易,然后,根据GroupBy
上对into_iter()
的多次调用会更改底层迭代器的事实,实现其next()
方法:
pub struct GroupByCount<I: Iterator> {
// This `fn(&K) -> K` could be `Box<dyn FnMut(&K) -> K`, but
// this adds one usize to the struct's size, and the function
// captures nothing.
inner: itertools::structs::GroupBy<I::Item, I, fn(&I::Item) -> I::Item>,
}
impl<I> Iterator for GroupByCount<I>
where
I: Iterator,
I::Item: PartialEq,
{
type Item = (I::Item, usize);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.into_iter()
.next()
.map(|(key, group)| (key, group.count()))
}
}
pub trait IteratorExt: Iterator {
fn group_by_count(self) -> GroupByCount<Self>
where
Self: Sized;
}
impl<I> IteratorExt for I
where
I: Iterator,
I::Item: Clone + PartialEq,
{
fn group_by_count(self) -> GroupByCount<Self>
where
Self: Sized,
{
GroupByCount {
inner: self.group_by(|i| i.clone()),
}
}
}
游乐场。
我做的其他改进:
- 删除冗余的
Sized
边界 - 删除现在冗余的
'static
边界 - 删除
Eq
绑定,因为这是不必要的 - 去掉类型参数
T
,改为显式使用I::Item
- 根据RFC 445,扩展特性约定,将
ExtIterator
重命名为IteratorExt