nom 解析器借用检查器问题

nom parser borrow checker issue

我有这个使用 nom 4.2.2 的 Rust 程序。 (我冒昧地扩展了 nom 解析器功能。)

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);
  }
}

使用 rustc 1.33.0 (2aa4c46cf 2019-02-28) 编译此程序会产生以下问题:

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

切换到注释掉的解析器版本就可以正常编译了。 my_parsernom_parser 有何不同?谁在借buf?我应该如何更改程序才能安抚借阅检查员?

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

您正在使用 ? 运算符将错误传播到 mainIResult<&[u8], String, u32> = Result<(&[u8], String), nom::Err<&[u8], u32>>。因此,如果出现错误,&buf 将作为它的一部分返回,因此即使在 main 函数退出后它也必须保持活动状态,但不会因为 buf 是 [= 内部的局部变量13=].

在你的情况下 nom_parser 永远不会 returns 错误,但验证只关心类型和函数签名。

要修复它,您应该在向上传播之前以某种方式处理错误。例如:

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

请注意 IResult 中的 Err 并不总是 硬错误 。它可能是 nom::Err::Incomplete,意味着如果提供更多数据,解析可能会成功,或者 nom::Err::Error 意味着输入与解析器不匹配(所以 alt! 中的另一个解析器可能会成功), 或 nom::Err::Failure, 意味着在解析过程中确实出了点问题。视情况而定,你可以认为它们都是失败的,或者区别对待。

问题似乎出现在 IResult<I, O, E = u32> 中,扩展到 Result<(I, O), Err<I, E>>

如您所见,当您使用 ? 时,您可能 return 的 Err 仍然可以包含对类型 I 的引用,即您的 &[u8],以及来自您的函数的 return。

函数 return 此引用的唯一方法是引用的生命周期不以函数结束,'static

解决您的问题的一个简单方法是将 &[u8] 更改为 Vec<u8>,即使我不确定您要用它做什么。