Search code examples
rustborrow-checker

Confusing Rust lifetime troubles when tring to implement an Iterator with a stored closure


I'm trying to write a simple text parsing utility working on a &str. I'd like to implement a function that accepts a closure object and a separator string, which returns a type that implements Iterator that continuously calls the closure object and eats the separator on each invocation of next()

Since I don't know what type the iterated item is (that is up to the closure), I'd like to generalize this, so it's signature will be FnMut(&mut Parser) -> Option<T>. Since it may optionally borrow from the string stored by the parser, I need to insert the appropriate lifetimes.

This is what I have so far:

(Link to Rust Playground)

struct Parser<'a> {
    string: &'a str,
}

impl<'a> Parser<'a> {
    // ...
    
    fn at_end(&self) -> bool {
        // returns whether we're at the end of the string
        false
    }

    fn expect(&mut self, _s: &str) -> &mut Self {
        // expects the next part to be `s` and advances the string
        // ...
        self
    }

    fn parse_iter<'b, T: 'a, F: FnMut(&'a mut Self) -> Option<T>>(
        &'a mut self,
        separator: &'b str,
        f: F,
    ) -> ParseIter<'a, 'b, T, F> {
        ParseIter {
            parser: self,
            func: f,
            sep: separator,
            skip: false,
        }
    }
}

struct ParseIter<'a, 'b, T, F>
where
    T: 'a,
    F: FnMut(&'a mut Parser<'a>) -> Option<T>
{
    parser: &'a mut Parser<'a>,
    func: F,
    sep: &'b str,
    skip: bool,
}

impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
where
    T: 'a,
    F: FnMut(&'a mut Parser<'a>) -> Option<T>
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if self.parser.at_end() {
            return None;
        }

        if std::mem::replace(&mut self.skip, true) {
            self.parser.expect(self.sep);
        }

        (self.func)(self.parser)   // error here!
    }
}

That last line of code, invoking the closure, produces an error:

error: lifetime may not live long enough
   --> util\src\parser.rs:156:9
    |
140 | impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
    |      -- lifetime `'a` defined here
...
147 |     fn next(&mut self) -> Option<Self::Item> {
    |             - let's call the lifetime of this reference `'1`
...
156 |         (self.func)(self.parser)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

I mean, I think I don't even understand the essence of the error. Clearly, &mut self cannot outlive 'a because it is itself an object that contains references that have a lifetime of 'a, so how can &mut self possibly outlive 'a?

But ok, taking the error at face value, I guess I need to make sure that &mut self is also 'a. But I can't explicitly name it, because that's not specified thus by the Iterator trait.

How can I make my code compile?

And I guess a secondary question, I've been peppering the code with explicit references to try to fix the issue. What's the minimum amount of explicit references I can get away with?


Solution

  • The problem stems from &'a mut Parser<'a>. This kind of pattern is always wrong. This can work with shared references (&'a Parser<'a>) because shared reference lifetimes are variant; exclusive reference lifetimes are not.

    Just remove the reference lifetime from the &mut Parser types where possible; introduce a new lifetime parameter where one is required.

    struct Parser<'a> {
        string: &'a str,
    }
    
    impl<'a> Parser<'a> {
        // ...
        
        fn at_end(&self) -> bool {
            // returns whether we're at the end of the string
            false
        }
    
        fn expect(&mut self, _s: &str) -> &mut Self {
            // expects the next part to be `s` and advances the string
            // ...
            self
        }
    
        fn parse_iter<'b, 'c, T: 'a, F: FnMut(&mut Self) -> Option<T>>(
            &'c mut self,
            separator: &'b str,
            f: F,
        ) -> ParseIter<'a, 'b, 'c, T, F> {
            ParseIter {
                parser: self,
                func: f,
                sep: separator,
                skip: false,
            }
        }
    }
    
    struct ParseIter<'a, 'b, 'c, T, F>
    where
        T: 'a,
        F: FnMut(&mut Parser<'a>) -> Option<T>
    {
        parser: &'c mut Parser<'a>,
        func: F,
        sep: &'b str,
        skip: bool,
    }
    
    impl<'a, 'b, 'c, T, F> Iterator for ParseIter<'a, 'b, 'c, T, F>
    where
        T: 'a,
        F: FnMut(&mut Parser<'a>) -> Option<T>
    {
        type Item = T;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.parser.at_end() {
                return None;
            }
    
            if std::mem::replace(&mut self.skip, true) {
                self.parser.expect(self.sep);
            }
    
            (self.func)(self.parser)   // error here!
        }
    }
    

    (Playground))