为包含可迭代字段的结构实现迭代器特征

人气:712 发布:2022-10-16 标签: iterator rust traits

问题描述

我想为包含可迭代字段的结构实现Iterator特征。在结构上迭代应该会产生与在字段上迭代得到的结果相同的结果。这就是我想要的(显然不起作用):

struct Foo {
    bar: Vec<char>,
}

impl Iterator for Foo {
    type Item: &char; // Error: expected named lifetime parameter
    
    fn next(&mut self) -> Option<Self::Item> {
        self.bar.iter().next()
    }
}

为避免该错误,我尝试插入生命周期:

use std::marker::PhantomData;

struct Foo<'a> {
    bar: Vec<char>,
    phantom: &'a PhantomData<char> // not sure what to put inside < .. >
}

impl<'a> Iterator for Foo<'a> {
    type Item = &'a char;

    fn next(&mut self) -> Option<Self::Item> {
        self.bar.iter().next() // again, several errors about lifetimes
    }
}

如何实现此类结构的Iterator特征?

推荐答案

创建Iterator的对象和创建Iterator的对象之间有很大的区别。例如,Vec<char>可以产生迭代器,但本身不是迭代器。这里有两个简单的示例,希望您可以找到适合您的用例的东西。

生成迭代器

对于您的情况,最简单的方法是只为该字段实现Deref,然后让Vec<char>处理它。或者,您也可以为bar.iter()编写包装函数。

pub struct Foo {
    bar: Vec<char>,
}

impl Deref for Foo {
    type Target = Vec<char>;

    fn deref(&self) -> &Self::Target {
        &self.bar
    }
}

let foo = Foo { bar: vec!['a', 'b', 'c', 'd'] };

// deref is implicitly called so foo.iter() represents foo.bar.iter()
for x in foo.iter() {
    println!("{:?}", x);
}

编写迭代器

以下是如何为Vec<char>编写自己的迭代器。请注意Vec如何存储为引用而不是拥有的值。这样才能解决生锈对寿命的制约。通过在迭代器的生存期内保持一个不可变的引用,我们可以保证该迭代器产生的引用也可以在该生存期内保持不变。如果我们使用拥有的值,我们只能保证元素引用的生存期持续到下次将可变引用带到迭代器时。换句话说,每个值只能持续到再次调用next。然而,即使是这样,也需要夜间功能正确表达。

pub struct SimpleIter<'a> {
    values: &'a Vec<char>,
    index: usize,
}

impl<'a> Iterator for SimpleIter<'a> {
    type Item = &'a char;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.index >= self.values.len() {
            return None
        }
        
        self.index += 1;
        Some(&self.values[self.index - 1])
    }
}

下面是一个通用迭代器包装另一个迭代器的简单示例。

// Add ?Sized so Foo can hold a dynamically sized type to satisfy IntoFoo
struct Foo<I: ?Sized> {
    bar: I,
}

impl<I: Iterator> Iterator for Foo<I> {
    type Item = <I as Iterator>::Item;
    
    fn next(&mut self) -> Option<Self::Item> {
        println!("Iterating through Foo");
        self.bar.next()
    }
}

您还可以通过为Foo创建一个易于使用的特征来更好地使用它。

pub trait IntoFoo {
    fn iter_foo(self) -> Foo<Self>;
}

// Add an iter_foo() method for all existing iterators
impl<T: Iterator> IntoFoo for T {
    fn iter_foo(self) -> Foo<Self> {
        Foo { bar: self }
    }
}


let values = vec!['a', 'b', 'c', 'd'];

// Get default iterator and wrap it with our foo iterator
let foo: Foo<std::slice::Iter<'_, char>> = values.iter().iter_foo();

for x in foo {
    println!("{:?}", x);
}

778