`cannot infer an appropriate lifetime` (indirect recursive function)

Hi,

For a day I have been trying to overcome the following lifetime problem.

I have this code:

(which can be executed via cargo-play. Unfortuantely, cssparser is not available in the Playground. But here is a link to a playground with the code)

(the relevant documentation for Parser::parse_nested_block can be found here)

//# cssparser = "0.28.1"

use cssparser::{ParseError, Parser, ParserInput, Token};

fn extract_css_classes<'i>(input: &'i str) {
    let input = ParserInput::new(input);
    let p = Parser::new(&mut input);
    let selectors = Vec::new();
    do_extract_css_classes(&mut p, &mut selectors);
}

// Underscores added for debugging:
fn do_extract_css_classes<'i_, 't_: 'i_>(
    parser: &'t_ mut Parser<'i_, 't_>,
    selectors: &'i_ Vec<&'i_ str>,
) {
    loop {
        if parser.is_exhausted() {
            return;
        }
        let token = parser.next().unwrap();
        println!("Token => {:?}", token);
        match token {
            Token::SquareBracketBlock
            | Token::CurlyBracketBlock
            | Token::ParenthesisBlock => {
                parser
                    .parse_nested_block(|p| {
                        do_extract_css_classes(p, selectors);
                        Ok::<(), ParseError<String>>(())
                    })
                    .unwrap();
            }
            _ => continue,
        }
    }
}

fn main() {
    let input = r#"
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        @media screen and (min-width: 900px) {
            article {
              padding: 1rem 3rem;
            }
          }
    "#;
    extract_css_classes(input);
}

Which results in the following error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'t_` due to conflicting requirements
  --> src/main.rs:28:25
   |
28 |                         do_extract_css_classes(p, selectors);
   |                         ^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #3 defined on the body at 27:41...
  --> src/main.rs:27:41
   |
27 |                       .parse_nested_block(|p| {
   |  _________________________________________^
28 | |                         do_extract_css_classes(p, selectors);
29 | |                         Ok::<(), ParseError<String>>(())
30 | |                     })
   | |_____________________^
note: ...so that the expression is assignable
  --> src/main.rs:28:48
   |
28 |                         do_extract_css_classes(p, selectors);
   |                                                ^
   = note: expected `&mut cssparser::Parser<'_, '_>`
              found `&mut cssparser::Parser<'_, '_>`
note: but, the lifetime must be valid for the lifetime `'i_` as defined on the function body at 12:27...
  --> src/main.rs:12:27
   |
12 | fn do_extract_css_classes<'i_, 't_: 'i_>(
   |                           ^^^
note: ...so that the types are compatible
  --> src/main.rs:27:22
   |
27 |                     .parse_nested_block(|p| {
   |                      ^^^^^^^^^^^^^^^^^^
   = note: expected `&mut cssparser::Parser<'_, '_>`
              found `&mut cssparser::Parser<'i_, 't_>`

Can anyone tell me how to overcome this problem? Nothing I tried worked.

Your code compiles if you remove every lifetime annotation, and fix mutability (add muts where needed and remove muts where superfluous):

fn extract_css_classes(input: &str) {
    let mut input = ParserInput::new(input);
    let mut p = Parser::new(&mut input);
    let mut selectors = Vec::new();
    do_extract_css_classes(&mut p, &mut selectors);
}

fn do_extract_css_classes(
    parser: &mut Parser,
    selectors: &Vec<&str>,
) {
    loop {
        if parser.is_exhausted() {
            return;
        }
        let token = parser.next().unwrap();
        println!("Token => {:?}", token);
        match token {
            Token::SquareBracketBlock
            | Token::CurlyBracketBlock
            | Token::ParenthesisBlock => {
                parser
                    .parse_nested_block(|p| {
                        do_extract_css_classes(p, selectors);
                        Ok::<(), ParseError<String>>(())
                    })
                    .unwrap();
            }
            _ => continue,
        }
    }
}

fn main() {
    let input = r#"
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        @media screen and (min-width: 900px) {
            article {
              padding: 1rem 3rem;
            }
          }
    "#;
    extract_css_classes(input);
}

Generally, I've come to the conclusion that too many lifetime annotations are a code smell, especially when you are duplicating/mirroring lifetime annotations from the functions you are using, instead of using the same lifetimes.

If you also want to return some tokens back to the caller, as opposed to only printing them inside the function, you have to add only a couple of lifetime annotations in order to let the compiler know that the tokens come from the parser:

use cssparser::{ParseError, Parser, ParserInput, Token};

fn extract_css_classes(input: &str) -> Vec<Token> {
    let mut input = ParserInput::new(input);
    let mut p = Parser::new(&mut input);
    let mut tokens = Vec::new();
    do_extract_css_classes(&mut p, &[], &mut tokens);
    tokens
}

fn do_extract_css_classes<'a>(
    parser: &mut Parser<'a, '_>,
    selectors: &[&str],
    out_tokens: &mut Vec<Token<'a>>
) {
    loop {
        if parser.is_exhausted() {
            return;
        }
        let token = parser.next().unwrap();
        out_tokens.push(token.clone());
        match token {
            Token::SquareBracketBlock
            | Token::CurlyBracketBlock
            | Token::ParenthesisBlock => {
                parser
                    .parse_nested_block(|p| {
                        do_extract_css_classes(p, selectors, out_tokens);
                        Ok::<(), ParseError<String>>(())
                    })
                    .unwrap();
            }
            _ => continue,
        }
    }
}

fn main() {
    let input = r#"
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        h1 .\32xl\:animate-bounce, foo[class*='bar'] {
            color: blue;
            font-size: 12px;
        };
        @media screen and (min-width: 900px) {
            article {
              padding: 1rem 3rem;
            }
          }
    "#;
    let tokens = extract_css_classes(input);
    println!("{:#?}", tokens);
}
2 Likes

@H2CO3: Thank you so much! Your second solution solved my problem.

Generally, I've come to the conclusion that too many lifetime annotations are a code smell, especially when you are duplicating/mirroring lifetime annotations from the functions you are using, instead of using the same lifetimes.

Thanks again. I try to keep this in mind.