【问题标题】:How can I add new methods to Iterator?如何向迭代器添加新方法?
【发布时间】:2015-08-12 23:31:54
【问题描述】:

我想在迭代器上定义一个.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`

我做错了什么?我将如何扩展这种任意的可散列类型?

【问题讨论】:

    标签: iterator rust


    【解决方案1】:

    在您的特定情况下,这是因为您已经为 String 的迭代器实现了特征,但您的向量提供了 &amp;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

    这个 trait 定义了 unique 方法,只有当迭代项可以是:

    1. 散列
    2. 比较完全平等
    3. 克隆

    此外,它要求实现Iterator 的项目在编译时具有已知大小。这样做是为了让Unique 迭代器适配器使用迭代器。

    另一个重要的部分是特性的一揽子实现,它适用于也实现Iterator的任何类型:

    impl<I: Iterator> UniqueExt for I {}
    

    【讨论】:

    • 完美,谢谢!我要把它扔到 crates.io 上,希望没问题。
    • @WilfredHughes 很好,但您可能希望将其贡献给itertools crate,这是一个很好的存储库,用于这些类型的添加。
    • 感谢@Shepmaster 的回答。这个例子帮助我解决了几个我在我读过的任何文档中都没有看到的问题——事实上,这是我记得阅读过如何创建通用扩展方法的唯一答案反对迭代器。非常感谢问答。
    猜你喜欢
    • 2011-05-06
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 2012-06-09
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    相关资源
    最近更新 更多