使用“anyhow”板条箱与“nom”板条箱结合使用的终身问题

Pit*_*nah 4 lifetime rust

我遇到了生命周期问题,我不确定如何解决,因为我所做的改变对于生命周期来说似乎是微不足道的。

鉴于:

use anyhow::Context;
use nom::{IResult, bytes::complete::tag};
Run Code Online (Sandbox Code Playgroud)

以下代码编译:

let input = std::str::from_utf8(&output.stdout).unwrap();

let mut lines = input.lines();
let branch_line = lines.next().context("no output from `git status`")?;
let branch: IResult<&str, &str> = tag("On branch ")(branch_line);
let (branch, _) = branch.expect("failed to get name of current branch");
Run Code Online (Sandbox Code Playgroud)

expect将最后一行中的更改为 后context,代码不再编译:

let input = std::str::from_utf8(&output.stdout).unwrap();

let mut lines = input.lines();
let branch_line = lines.next().context("no output from `git status`")?;
let branch: IResult<&str, &str> = tag("On branch ")(branch_line);
let (branch, _) = branch.context("failed to get name of current branch")?;
Run Code Online (Sandbox Code Playgroud)
error[E0597]: `output.stdout` does not live long enough
   --> src/status.rs:303:41
    |
303 |         let input = std::str::from_utf8(&output.stdout).unwrap();
    |                                         ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
307 |         let branch: IResult<&str, &str> = tag("On branch ")(branch_line);
    |                     ------------------- type annotation requires that `output.stdout` is borrowed for `'static`
...
436 |     }
    |     - `output.stdout` dropped here while still borrowed
Run Code Online (Sandbox Code Playgroud)

查看 的文档anyhow,在我看来,它不应该在&output.stdout.

fn context<C>(self, context: C) -> Result<T, Error>
where
    C: Display + Send + Sync + 'static, 
Run Code Online (Sandbox Code Playgroud)

挠我的头。对一生来说仍然是新鲜事。

Fin*_*nis 6

问题是 的类型branch,即IResult<&str, &str>

如果您查看实现,您可以看到这是 的别名IResult<&str, &str, nom::error::Error<&str>>,它又是 的别名Result<(&str, &str), nom::internal::err<nom::error::Error<&str>>>

这看起来很复杂,但我想说的要点是它branch是一种Result类型,并且Err它的情况具有类型nom::internal::err<nom::error::Error<&str>>。换句话说,错误带有&str.

这是故意的,因为所有权对于 nom 来说是一个大问题。这与当前借用检查器的这些已知问题密切相关。Nom 通过类型返回所有​​权来解决这个问题Err

遗憾的是,这意味着它与anyhow. 的错误类型nom旨在由 消耗nom,或者至少在引发到用户代码之前手动转换为其他内容。

为了解释您收到的确切错误:

  • output.stdout是一个局部变量
  • input以及其背后的其他所有内容都引用了中的数据output.stdout
  • Err变体branch仍在引用output.stdout
  • .context(),或者更准确地说,它的后面,尝试从函数中?返回变体,但失败了,因为它仍然引用,并且该引用将比它引用的数据更长寿。Erroutput.stdout

这对于 来说不是问题next().context(),因为None的值next()包含对 的引用output.stdout

&str解决此问题的一种方法是通过将类型转换Err为拥有的来破坏引用String

use anyhow::{Context, Result};
use nom::{bytes::complete::tag, IResult};

fn main() -> Result<()> {
    let input = "aaaaaa".to_string();

    let mut lines = input.lines();
    let branch_line = lines.next().context("no output from `git status`")?;
    let branch: IResult<&str, &str> = tag("On branch ")(branch_line);
    let (branch, _) = branch
        .map_err(|e| e.to_owned())
        .context("failed to get name of current branch")?;

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)