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

问题描述 投票:0回答:1

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

鉴于:

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

以下代码编译:

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

将最后一行中的

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")?;
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

查看

anyhow
的文档,在我看来,它不应该在
&output.stdout
上引入任何生命周期限制。

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

挠头。对一生来说还是新鲜事。

rust lifetime
1个回答
4
投票

问题在于

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()
    ,或者更准确地说,它后面的
    ?
    ,尝试从函数中返回
    Err
    变体,但失败了,因为它仍然引用
    output.stdout
    ,并且该引用将比它的数据更长寿。参考资料。

这对于

next().context()
来说不是问题,因为
None
next()
值确实 not 带有对
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(())
}
© www.soinside.com 2019 - 2024. All rights reserved.