rustparser-combinatorsnom

nom & borrowed value does not live long enough error


Trying to work with nom and iterate over my result, but I can't figure out why this borrowed value doesn't live long enough. Still new to Rust and have been head-banging for hours. Would appreciate help!

use anyhow;
use nom::{
    bytes::complete::{tag, take},
    character::complete::newline,
    combinator::rest,
    multi::separated_list1,
    sequence::separated_pair,
    IResult,
};

pub fn parse(raw: String) -> anyhow::Result<()> {
    let (_, lines) = parse_multiline(&raw)?;

    for line in lines.iter() {
        let (label, value) = line;

        println!("label: {}, value: {}", label, value);
    }

    Ok(())
}

fn parse_multiline(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
    separated_list1(
        newline,
        separated_pair(
            take(14usize),
            tag(" : "),
            rest,
        ),
    )(i)
}

And the error:

error[E0597]: `raw` does not live long enough
  --> src/parser/person.rs:12:38
   |
12 |     let (_, lines) = parse_multiline(&raw)?;
   |                      ----------------^^^^-
   |                      |               |
   |                      |               borrowed value does not live long enough
   |                      argument requires that `raw` is borrowed for `'static`
...
21 | }
   | - `raw` dropped here while still borrowed

Solution

  • In parse_multiline() you return IResult<&str, Vec<(&str, &str)>>. This means that if your parse_multiline() fails, then it returns nom::Err<nom::error::Error<&str>> which has a reference to i/&raw.

    Thus in parse(), parse_multiline(raw)? propagates that error further, which would return a reference to raw, which would not live long enough.


    If you want to retain fn parse(raw: String), then the you can use Result::map_err() and then nom::Err::map(), to convert the &str into an owned String on error.

    // nom = "6.1"
    use nom::error::Error;
    
    pub fn parse(raw: String) -> anyhow::Result<()> {
        let (_, lines) = parse_multiline(&raw)
            .map_err(|err| err.map(|err| Error::new(err.input.to_string(), err.code)))?;
    
        // ...
    }