我如何习惯性地用函数而不是闭包来解包Rust错误?

pb3s4cty  于 2023-04-30  发布在  其他
关注(0)|答案(2)|浏览(124)

我在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,而不是什么!。我可以欺骗并将fail()的返回值更改为-> Value,并在exit(1)之后添加Value::Null。它工作,但感觉错误(并抱怨)。
我也可以做unwrap_or_else(|e| fail(e)),这并不可怕。
有没有一个习惯的方法来处理这个问题?

vh0rcniy

vh0rcniy1#

正如@kmdreko所指出的,代码编译失败是因为!可以强制到任何Tfn() -> !不能强制到fn() -> T
要解决上述问题,您可以声明fail()以返回Value,实际上返回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

bvhaajcl

bvhaajcl2#

我不确定这是否符合Rust的习惯,但你可以把所有的东西放在一个闭包中,然后处理Result

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

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)
    })().or_else(|err: Box<dyn Error>| {
        eprintln!("Error in get_json_response(): failed to get: {}", err);
        std::process::exit(1);
        // or just return Err(err) if you don't want to exit
    })    
}

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

相关问题