Incombatible lifetimes for Iterator implementations when owning a "generator object"


#1

Hi everyone,

I have a problem with lifetimes and iterators. Given a data-structure like

    pub struct Generator;

    impl Generator {
        fn items<'a>(&'a self) -> Box<Iterator<Item=u32> + 'a> {
            Box::new(0u32..10u32)
        }
    }

    pub struct ContainerOwned<'a> {
        it: Option<Box<Iterator<Item=u32> + 'a>>,
        gen: Generator,
    }

I would like it to implement Iterator in a way that the it field is updated in the next(...) function by calling a function on gen:

    impl<'a> Iterator for ContainerOwned<'a> {
        type Item=u32;
        
        fn next(&mut self) -> Option<u32> {
            if self.it.is_none() {
                // Compiler error here:
                self.it = Some(self.gen.items());
            }
            
            if let Some(ref mut it) = self.it {
                return it.next();
            }
            None
        }
    }

This will not compile with a “conflicting requirements” error message (full playground example: https://play.rust-lang.org/?gist=243dac35dc8f072473a2be9b2f15126f&version=stable)

However, if I use a reference as field like in

    pub struct ContainerReference<'a> {
        it: Option<Box<Iterator<Item=u32> + 'a>>,
        gen: &'a Generator,
    }

it works fine (full playground example https://play.rust-lang.org/?gist=ebae6e0a325bebcb9afa3b574d3e7b02&version=stable)

Currently I’m stucked in trying to understand why the reference works but not the owned one not. Do you have any ideas?


#2

The case that doesn’t work boils down to a self-referential struct - this comes up from time to time in Rust. In short (google/search for more details), a struct cannot have references to itself; self.it = Some(self.gen.items()) would create that situation because gen is owned by ContainerOwned and items() returns an iterator that in turn yields references with a lifetime tied to gen.

If you did (note the 'static lifetime of the iterator):

impl Generator {
    fn items<'a>(&'a self) -> Box<Iterator<Item=u32> + 'static> {
        Box::new(0u32..10u32)
    }
}

then it would work.


#3

Thank you for you response! I did have some issues with self-references in other parts of my code which I could resolve, but I totally missed that this is same problem here.