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

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()),
        }
    }
}

Playground.

我所做的其他改进:

  • 删除多余的 Sized 边界。
  • 删除 now-redundant 'static 边界。
  • 删除 Eq 绑定,因为它是不必要的。
  • 删除类型参数 T 并显式使用 I::Item.
  • 根据 RFC 445, Extension Trait Conventions.
  • ExtIterator 重命名为 IteratorExt