Search code examples
rustlifetime

Borrow and internal IntoIterator


I though I got smart with lifetimes, but the compiler teaches me again...

error[E0521]: borrowed data escapes outside of method

play


pub struct Bugger<S> {
    pub items: S,
}
impl<S> Bugger<S>
where
    &'static S: IntoIterator + 'static,
    <&'static S as IntoIterator>::Item: AsRef<u8>,
{
    fn do_something_with_items(&self) -> impl  Iterator<Item = u8> {
        (&self.items).into_iter().map(|b| *b.as_ref())
    }
}

I want to accept a generic member that implements the IntoIterator on it's reference and then use it in another method, actually a trait impl, but even this fails.


Solution

  • The primary reason this fails to compile is because you take &self (which must be valid for any lifetime) but the bound on the implementation block requires IntoIterator to be implemented for a reference with 'static lifetime. The way you use this bound in the method therefore requires that the &self reference also be 'static, but you've not indicated this. You can fix this specific example by having the method take &'static self.

    However, this is likely not what you want anyway. Most likely, you want a lifetime shorter than 'static. You can describe this in the trait bound with a higher-rank trait bound (HRTB).

    You also need to indicate in the return type that the returned iterator borrows from self, which you can do by adding the anonymous lifetime '_ to the bound.

    pub struct Bugger<S> {
        pub items: S,
    }
    
    impl<S> Bugger<S>
    where
        for<'a> &'a S: IntoIterator,
        for<'a> <&'a S as IntoIterator>::Item: AsRef<u8>,
    {
        fn do_something_with_items(&self) -> impl Iterator<Item = u8> + '_ {
            (&self.items).into_iter().map(|b| *b.as_ref())
        }
    }
    

    (Playground)

    Note that the anonymous lifetime is just syntactic sugar for this:

    fn do_something_with_items<'a>(&'a self) -> impl Iterator<Item = u8> + 'a