我想在迭代器上定义一个.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`
我做错了什么?我该如何扩展这种任意的哈希类型?
答案 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
方法,该方法仅在迭代项可以是以下时调用才有效:
此外,它要求实现Iterator
的项在编译时具有已知大小。这样做是为了使Unique
迭代器适配器可以消耗迭代器。
另一个重要的部分是任何类型的毯子实现,它也实现了Iterator
:
impl<I: Iterator> UniqueExt for I {}