Lifetime confusing in closure. Why?

Hi!
I got this code:

...

impl<'a, M> Parser<'a> for ElementsSet<'a, M>
where
    M: Fn(&'a str) -> String,
{
    fn parse(&self, in_string: &'a str) -> (Result<String, ()>, &'a str) {
        let res = self.parser.parse(in_string);
        return if let Ok(r) = res.0 {
            (Ok((self.mapper)(&r)), res.1)
        } else {
            res
        };
    }
}

...

impl<'a> Parser<'a> for ElementAny {
    fn parse(&self, in_string: &'a str) -> (Result<String, ()>, &'a str) {
        let mut parser = And::new();

        parser.add_parser(BoxedParser::new(ElementOpen::new()));
        parser.add_parser(BoxedParser::new(ZeroOrMore::new(ElementsSet::new(
            |parsed| {
                let ident = " ".repeat(self.level * 4).to_string();
                ident
            },
            self.level + 1,
        ))));
        parser.add_parser(BoxedParser::new(ElementClose::new()));

        return parser.parse(in_string);
    }
}

And I got this compiler error

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> src/xml_formater.rs:453:13
    |
453 | /             |parsed| {
454 | |                 let ident = " ".repeat(self.level * 4).to_string();
455 | |                 ident
456 | |             },
    | |_____________^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 448:5...
   --> src/xml_formater.rs:448:5
    |
448 | /     fn parse(&self, in_string: &'a str) -> (Result<String, ()>, &'a str) {
449 | |         let mut parser = And::new();
450 | |
451 | |         parser.add_parser(BoxedParser::new(ElementOpen::new()));
...   |
461 | |         return parser.parse(in_string);
462 | |     }
    | |_____^
    = note: ...so that the types are compatible:
            expected &&xml_formater::ElementAny
               found &&xml_formater::ElementAny
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 447:6...
   --> src/xml_formater.rs:447:6
    |
447 | impl<'a> Parser<'a> for ElementAny {
    |      ^^
    = note: ...so that the expression is assignable:
            expected (std::result::Result<std::string::String, ()>, &'a str)
               found (std::result::Result<std::string::String, ()>, &str)

I don't understand a reason of the error! Which variable lifetime rust can't infer? ident is a local variable and it's not borrowed. parsed even not be used at now.
What's wrong?

Think would need more code to try out on playground.

I would try making sure it does not have return lifetime bug;

let r = parser.parse(in_string);
return r;

Checking the parser;

let mut parser = And::new();
let _: &dyn Parser<'a> = &parser;

Adding explicit lifetime to closures parameter.

|parsed: &'a str| {

Make sure it compiles without the calls to add_parser.

I have figured out that mistake is in closure itself.

|parsed| {
                let ident = " ".repeat(self.level * 4).to_string();
                ident
            }

It is because of using self.level field. But I still can't understand - why?

It's trying to borrow self when self doesn't live long enough. To confirm this, can you post the definition of And and And::add_parser

Here is the fix

let level = self.level:
move |parsed| {
                let ident = " ".repeat(level * 4).to_string();
                ident
            }
1 Like

Thanks! Something start to make cleaner.
But I don't understand - parser variable live to the and of parse function. Closure - same, because parse is it's owner. Why it isn't correct?

I need to see the type definitions to answer that

This is it.

What's BoxedParser?

Wrapper to make a list of different type parsers.

struct BoxedParser<'a> {
    parser: Box<dyn Parser<'a> + 'a>,
}

And that's why, your lifetimes are wired together incorrectly

struct BoxedParser<'a, 'env> {
    parser: Box<dyn Parser<'a> + 'env>,
}

Those two lifetimes are not (directly) related, so you shouldn't use the same lifetime parameter for both.

2 Likes

Hm... Interesting. I'll think about it. Can't understand right now.

I fixed closure, but now one more trouble. Can you help?

error[E0597]: `r` does not live long enough
   --> src/xml_formater.rs:430:36
    |
423 | impl<'a, 'b, M> Parser<'a> for ElementsSet<'a, M>
    |          -- lifetime `'b` defined here
...
430 |             let r1 = (self.mapper)(&r);
    |                      --------------^^-
    |                      |             |
    |                      |             borrowed value does not live long enough
    |                      argument requires that `r` is borrowed for `'b`
431 |             (Ok(r1), res.1)
432 |         } else {
    |         - `r` dropped here while still borrowed

Code is here.

You should use higher rank trait bounds (HRTBs)

for<'b> F: Fn(&'b str) -> String

That way the function F can use any lifetime.

1 Like

I did't know about this at all. Thanks!!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.