在迭代器扩展中处理生命类型
handling lifetypes in iterator extension
我正在尝试使用 group_by_count
方法扩展迭代器:
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
边界。
- 删除 now-redundant
'static
边界。
- 删除
Eq
绑定,因为它是不必要的。
- 删除类型参数
T
并显式使用 I::Item
.
- 根据 RFC 445, Extension Trait Conventions.
将 ExtIterator
重命名为 IteratorExt
我正在尝试使用 group_by_count
方法扩展迭代器:
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
边界。 - 删除 now-redundant
'static
边界。 - 删除
Eq
绑定,因为它是不必要的。 - 删除类型参数
T
并显式使用I::Item
. - 根据 RFC 445, Extension Trait Conventions. 将
ExtIterator
重命名为 IteratorExt