微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为包含可迭代字段的结构实现 Iterator trait

我想为包含可迭代字段的结构实现 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 如何存储为引用而不是拥有的值。这就是 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','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);
}
,

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

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

如果您想提供迭代功能,我建议添加一个 iter() 函数,该函数可以返回一个 std::slice::IterVec::iter() 返回)。

struct Foo {
    bar: Vec<char>,}

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。