Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nom parser borrow checker issue

I have this Rust program using nom 4.2.2. (I have taken the liberty of expanding the nom parser function.)

extern crate failure;
extern crate nom;

use failure::Error;
use std::fs::File;
use std::io::Read;

fn nom_parser(i: &[u8]) -> ::nom::IResult<&[u8], String, u32> {
    { ::nom::lib::std::result::Result::Ok((i, ("foo".to_owned()))) }
}

fn my_parser(buf: &[u8]) -> Result<(&[u8], String), Error> {
  Ok((buf, "foo".to_owned()))
}

fn main() -> Result<(), Error> {
  let handler = |mut entries: String| { entries.clear() };
  loop {
    let mut buf = Vec::new();
    File::open("/etc/hosts")?.read_to_end(&mut buf)?;
    let res = nom_parser(&buf)?.1;
    // let res = my_parser(&buf)?.1;
    handler(res);
  }
}

Compiling this program with rustc 1.33.0 (2aa4c46cf 2019-02-28) yields the following issue:

error[E0597]: `buf` does not live long enough
  --> nom-parsing/src/main.rs:21:26
   |
21 |     let res = nom_parser(&buf)?.1;
   |               -----------^^^^-
   |               |          |
   |               |          borrowed value does not live long enough
   |               argument requires that `buf` is borrowed for `'static`
...
24 |   }
   |   - `buf` dropped here while still borrowed

Switching to the commented out version of the parser compiles just fine. How are my_parser and nom_parser different? Who is borrowing buf? How should I change the program to placate the borrow checker?

like image 879
Bittrance Avatar asked Jan 26 '23 14:01

Bittrance


1 Answers

let res = nom_parser(&buf)?.1;
                          ^ here

You are using the ? operator to propagate the error out of main. The IResult<&[u8], String, u32> = Result<(&[u8], String), nom::Err<&[u8], u32>>. So in case of error the &buf is returned as part of it, so it must stay alive even after main function exits, but it won't because buf is local variable inside main.

In your case the nom_parser never returns error, but the validation only cares about the types and function signatures.

To fix it, you should process the error somehow before propagating it up. For example:

let res = nom_parser(&buf).map_err(|_| failure::format_err!("Parsing failed!"))?.1;

Note that Err in the IResult is not always hard error. It could be nom::Err::Incomplete, meaning that the parsing may succeed if more data is supplied, or nom::Err::Error meaning that the input was not matched by the parser (so perhaps another parser in alt! could succeed), or nom::Err::Failure, meaning that something went really wrong during parsing. Depending on the situation, you may consider them all as failure, or handle them differently.

like image 126
michalsrb Avatar answered Feb 07 '23 17:02

michalsrb