我如何使用函数而不是闭包以惯用的方式解包 Rust 错误?

How do I idiomatically unwrap Rust errors with functions rather than closures?

我正在努力在 Rust 中干净地处理错误。假设我有一个使用 Box<dyn Error> 传播多种错误类型的函数。要解包并处理错误,我正在执行以下操作:

fn main() {
    let json =
        get_json_response(format!("{}{}", BASE_URL, LOGIN_URL).as_str()).unwrap_or_else(|e| {
            eprintln!("Error: failed to get: {}", e);
            std::process::exit(1);
        });
}

fn get_json_response(url: &str) -> Result<Value, Box<dyn Error>> {
    let resp = ureq::get(url)
        .set("Authorization", format!("Bearer {}", API_TOKEN).as_str())
        .call()?
        .into_json()?;
    Ok(resp)
}

这很好用。但是,如果我多次调用 get_json_response(),一遍又一遍地包含相同的闭包会变得很混乱。

我的解决办法是改成:

use serde_json::Value;
use std::error::Error;
use ureq;

fn main() {
    let json =
        get_json_response(format!("{}{}", BASE_URL, LOGIN_URL).as_str()).unwrap_or_else(fail);
}

fn fail(err: Box<dyn Error>) -> ! {
    eprintln!("Error: failed to get: {}", err);
    std::process::exit(1);
}

这是行不通的,因为 unwrap_or_else() 期望 Value 被 return 编辑,而不是什么都没有 !。我可以作弊,把fail()的return值改成-> Value,在exit(1)后面加上Value::Null。它有效但感觉不对(并抱怨)。

我也可以 unwrap_or_else(|e| fail(e)) 这并不可怕。

有没有惯用的方法来处理这个问题?

作为@kmdreko 的 ,您的代码无法编译,因为虽然 ! 可以被强制转换为任何 T,但 fn() -> ! 不能被强制转换为 fn() -> T.

要解决上述问题,您可以将 fail() 声明为 return Value,实际上 return 是 std::process::exit(1) 的“值”。省略分号会将 ! 强制为 Value,并且您不必使用 Value::Null:

作弊
fn main() {
    let _json = get_json_response("...").unwrap_or_else(fail).as_str();
}

fn fail(err: Box<dyn Error>) -> Value {
    eprintln!("Error: failed to get: {}", err);
    std::process::exit(1)
}

Playground