如何向迭代器添加新方法?

时间:2015-05-29 23:59:40

标签: iterator rust

我想在迭代器上定义一个.unique()方法,使我能够迭代而不重复。

use std::collections::HashSet;

struct UniqueState<'a> {
    seen: HashSet<String>,
    underlying: &'a mut Iterator<Item = String>,
}

trait Unique {
    fn unique(&mut self) -> UniqueState;
}

impl Unique for Iterator<Item = String> {
    fn unique(&mut self) -> UniqueState {
        UniqueState {
            seen: HashSet::new(),
            underlying: self,
        }
    }
}

impl<'a> Iterator for UniqueState<'a> {
    type Item = String;
    fn next(&mut self) -> Option<String> {
        while let Some(x) = self.underlying.next() {
            if !self.seen.contains(&x) {
                self.seen.insert(x.clone());
                return Some(x);
            }
        }
        None
    }
}

这个编译。但是,当我尝试在同一个文件中使用时:

fn main() {
    let foo = vec!["a", "b", "a", "cc", "cc", "d"];

    for s in foo.iter().unique() {
        println!("{}", s);
    }
}

我收到以下错误:

error[E0599]: no method named `unique` found for type `std::slice::Iter<'_, &str>` in the current scope
  --> src/main.rs:37:25
   |
37 |     for s in foo.iter().unique() {
   |                         ^^^^^^
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `unique`, perhaps you need to implement it:
           candidate #1: `Unique`

我做错了什么?我该如何扩展这种任意的哈希类型?

1 个答案:

答案 0 :(得分:20)

在您的特定情况下,这是因为您已经为String的迭代器实现了特征,但是您的向量提供了&str的迭代器。这是一个更通用的版本:

use std::collections::HashSet;
use std::hash::Hash;

struct Unique<I>
where
    I: Iterator,
{
    seen: HashSet<I::Item>,
    underlying: I,
}

impl<I> Iterator for Unique<I>
where
    I: Iterator,
    I::Item: Hash + Eq + Clone,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(x) = self.underlying.next() {
            if !self.seen.contains(&x) {
                self.seen.insert(x.clone());
                return Some(x);
            }
        }
        None
    }
}

trait UniqueExt: Iterator {
    fn unique(self) -> Unique<Self>
    where
        Self::Item: Hash + Eq + Clone,
        Self: Sized,
    {
        Unique {
            seen: HashSet::new(),
            underlying: self,
        }
    }
}

impl<I: Iterator> UniqueExt for I {}

fn main() {
    let foo = vec!["a", "b", "a", "cc", "cc", "d"];

    for s in foo.iter().unique() {
        println!("{}", s);
    }
}

从广义上讲,我们创建了一个名为UniqueExt的新扩展特征,其中Iterator为超级特征。如果Iterator是超级用户,我们将可以访问相关类型 Iterator::Item

此特征定义了unique方法,该方法仅在迭代项可以是以下时调用才有效:

  1. 散列
  2. 比较完全平等
  3. 克隆
  4. 此外,它要求实现Iterator的项在编译时具有已知大小。这样做是为了使Unique迭代器适配器可以消耗迭代器。

    另一个重要的部分是任何类型的毯子实现,它也实现了Iterator

    impl<I: Iterator> UniqueExt for I {}