Borrowed value does not live long enough on Nom parser

Hi all,

I am trying to write a simple Nom parser to read through a file from disk.
To this end I've set up a public function to be used by the rest of the application, and an inner function to do the parsing. However, as the header suggests, I'm running into a lifetime error.

This is the code I'm running:

pub fn parse_template<P: AsRef<std::path::Path> + std::fmt::Debug + ?Sized>(filepath: &P) -> Result<(Setup, File)> {
    let mut buffer: String = String::new();

    std::fs::File::open(filepath)
        .with_context(|| format!("Failed whilst parsing, could not read file: {filepath:?}"))?
        .read_to_string(&mut buffer)?;

    parse_setup(&buffer)?;

    return Ok((Setup::default(), File::default()));
}

fn parse_setup(i: &str) -> nom::IResult<&str, &str> {
    let (i, _) = tag("/*")(i)?;
    let (i, _) = multispace0(i)?;
    let (i, _) = tag("setup:")(i)?;

    todo!();
}

With File and Setup being simple strings with a few Strings in them

This is the compiler diagnostic I'm getting:

error[E0597]: `buffer` does not live long enough
  --> src\parse.rs:14:17
   |
8  |     let mut buffer: String = String::new();
   |         ---------- binding `buffer` declared here
...
14 |     parse_setup(&buffer)?;
   |     ------------^^^^^^^-
   |     |           |
   |     |           borrowed value does not live long enough
   |     argument requires that `buffer` is borrowed for `'static`
...
17 | }
   | - `buffer` dropped here while still borrowed

Considering I'm reading the file in right there, I'm not sure how to make the buffer live long enough to be parsed.

I have tried cloning the string, calling .to_owned(), and calling .as_str(), but none seem to have the desired effect.

The nom::IResult::Err case borrows from the input, but you need something non-borrowing for your anyhow::Result. Map the error to something non-borrowing.

    // Example (that results in poor error messages)
    parse_setup(&buffer)
        .map_err(|err| anyhow!("nom error: {err:?}"))?;
4 Likes

That fixed it, thank you for your help!
I think that would've taken me quite a while to find (if I would've found it at all:sweat_smile:)!

1 Like

In the specific case of nom, its error type has a to_owned method to help out with this, keeping the rest of the error intact:

parse_setup(&buffer)
    .map_err(|err| err.to_owned())?;

also fixes the issue, while making it possible to migrate away from anyhow or match on the error later.

In this situation, it's not immediately needed, but if (for example) you decided to downcast the Error in order to give a better message when it's a common mistake, this technique keeps the nom error intact.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.