为包含可迭代字段的结构体实现 Iterator trait

13

我想为一个包含可迭代字段的结构体实现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特性?

1
如果可能的话,您可能希望实现Deref。这样,您就可以在不编写结构体包装器的情况下公开字段的方法(包括.iter())。 - Locke
4个回答

25

创建一个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 存储为引用而不是拥有值。这是因为 Rust 可以解决生命周期约束。通过在迭代器的生命周期内保持一个不可变的引用,我们可以保证此迭代器生成的引用也可以持续存在于该生命周期中。如果我们使用拥有值,我们只能保证元素引用的生命周期持续到下一次对迭代器进行可变引用时。换句话说,每个值只能持续到再次调用 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);
}

3

迭代器是一个有状态的对象,它记住了底层集合中的位置。在您的示例中,每次调用next()都会从开始重新迭代并返回第一个元素(前提是修复了错误)。

Foo实现Iterator意味着每次调用next()时实例本身会发生变化,这就是为什么迭代器通常具有自己的结构体。

如果您想提供迭代能力,我建议添加一个iter()函数,它可以返回一个std::slice::Iter(与Vec::iter()相同)。

struct Foo {
    bar: Vec<char>,
}

impl Foo { 
    pub fn iter(&self) -> impl Iterator<Item=&char> + '_ {
        self.bar.iter()
    }
}

0

我在我的结构体上使用了#[readonly::make]。这不会对只读造成问题,而且似乎很容易:

use std::slice::Iter;

pub fn iter(&self) -> Iter<Foo> {
    self.bar.iter()
}

这可能会违反某些规定,但我在简单的代码中使用它没有问题。


0

这是我一个调皮的实现。只是因为我刚接触 Rust :)

fn main() {
    let stream = ScoreStream::new(vec![32, 21, 37]);

    let i = stream.into_iter();
    for a in i {
        print!("\n {}", a);
    }
}

struct ScoreStream {
    vals: Vec<i32>,
    count: usize,
}

impl ScoreStream {
    fn new(vals: Vec<i32>) -> Self {
        Self { vals, count: 0 }
    }
}

impl Iterator for ScoreStream {
    type Item = i32;
    fn next(&mut self) -> Option<Self::Item> {
        let res = self.vals.get(self.count);
        match res {
            Some(v) => {
                self.count += 1;
                Some(*v)
            },
            None => None,
        }
    }
}



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接