Search code examples
rustiteratortraits

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?


Solution

  • 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);
    }