简体   繁体   中英

Implement Iterator trait for a struct containing an iterable field

I want to implement the Iterator trait for a struct which contains an iterable field. Iterating over my struct should yield the same results obtained iterating over the field. This is what I'd like to have (obviously not working):

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()
    }
}

To avoid that error I tried to insert lifetimes:

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
    }
}

How do I implement the Iterator trait for such a struct?

There is a big difference between something that creates an Iterator and something which is an Iterator . For example, Vec<char> can produce an iterator, but is not an iterator itself. Here are a couple simple examples of both so you can hopefully find something that fits your use case.

Producing an iterator

The easiest approach for your situation is to just implement Deref for the field and let Vec<char> handle it. Alternatively you could write a wrapper function for 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);
}

Writing an iterator

Here is how you can write your own iterator for a Vec<char> . Note how the Vec stores as a reference instead of an owned value. This is so rust can resolve the lifetime constraints. By holding an immutable reference for the lifetime of the iterator, we are guaranteeing that the references produced by this iterator can also persist for that lifespan. If we used an owned value, we can only guarantee that the lifetime of an element reference lasts until the next time a mutable reference is taken to the iterator. Or in other words, each value can only last until next is called again. However even this requires nightly features to properly express.

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])
    }
}

Here is a simple example of a generic iterator wrapping another iterator.

// 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()
    }
}

You can also get a bit more fancy with it by making a trait for easy usage of 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);
}

An iterator is a stateful object which remembers its position in the underlying collection. In your example, each call to next() would restart iterating from the start and return the first element (provided the errors were fixed).

Implementing Iterator for Foo would mean that the instance itself would have change at each call to next() , that's why iterators usually have their own struct.

If you want to provide iteration capability, I would advise adding an iter() function which can return a std::slice::Iter (which Vec::iter() returns).

struct Foo {
    bar: Vec<char>,
}

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

Here is a cheeky implementation by moi. Just because i'm new to 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,
        }
    }
}


I have #[readonly::make] on my structs. This will not cause a problem with readonly, and seems easy:

use std::slice::Iter;

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

It may violate something, but I have used it in simple code without a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM