在迭代器扩展中处理生命类型



我正试图用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

最新更新