解开选项并在 None 时返回错误的最简单方法(无论如何)

at5*_*321 24 error-handling rust

我正在使用Anyhow并有一个返回的函数anyhow::Result。在该函数中,我想“解开”一个选项,以便如果选项值为None,则返回带有特定错误消息的错误。这就是我所做的:

let o: Option<i32> = ...;
let v: i32 = o.ok_or_else(|| anyhow!("Oh, boy!"))?;
// here I need v only
Run Code Online (Sandbox Code Playgroud)

ok_or_else()出于ok_or()性能原因,我使用了 not 。

我通常对此表示同意,但想知道这是否是做我想做的事情的最简单(最简洁)的方法(不牺牲性能)?

egg*_*yal 38

你应该使用context游乐场):

use anyhow::{Context, Result};

fn _foo(o: Option<i32>) -> Result<i32> {
    let v = o.context("Oh, boy!")?;
    Ok(v)
}
Run Code Online (Sandbox Code Playgroud)

  • 我发现这个“上下文”名称有点神秘。 (5认同)
  • 棒极了!谢谢! (2认同)

at5*_*321 7

这个问题越来越受欢迎,所以我决定,作为 Eggyal 答案的补充,描述该context()方法如何在“幕后”实际工作,以便它可以方便地用于Option这样的值:some_opt.context("some error info here")?


context()

首先,无论如何Context公开一个具有方法的公共特征context()

pub trait Context<T, E>: Sealed {
    fn context<C>(self, context: C) -> Result<T, Error>
       where C: Display + Send + Sync + 'static;
Run Code Online (Sandbox Code Playgroud)

Context特征是为了实现的,Option并且实现如下所示:

impl<T> Context<T, Infallible> for Option<T> {
    fn context<C>(self, context: C) -> Result<T, Error>
    where
        C: Display + Send + Sync + 'static,
    {
        match self {
            Some(ok) => Ok(ok),
            None => Err(Error::from_display(context, backtrace!())),
        }
    }
Run Code Online (Sandbox Code Playgroud)

内部的实现from_display()有点复杂。它利用不安全Error::construct()并返回一个anyhow::Error值。


with_context()

Context还定义了一个类似的方法with_context(),可用于提供延迟评估的上下文(仅当错误实际发生时)。例子:

some_opt.with_context(|| format!("User {u} has no money"))?
Run Code Online (Sandbox Code Playgroud)

除了“懒惰”之外, 的实现本质with_context()上与 的实现相同context()


与比较ok_or_else(|| anyhow!("..."))

值得注意的是,在anyhow生成的堆栈(返回)跟踪中(当使用nightlyfeatures = ["backtrace"]时),如果我们使用表达式ok_or_else(|| anyhow!("...")),顶部会有一个额外的元素,如下所示:

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
Run Code Online (Sandbox Code Playgroud)

例如,如果我们使用bail!or ,也会发生同样的情况。ensure!

当我们使用context()or时context_with(),堆栈跟踪中没有这样的行。