有没有一种简化将Option转换为Result的方法,而不需要使用宏?

118

我有类似这样的东西(实际函数是来自rust-iniIni::Section::get):

impl Foo {
    pub fn get<K>(&'a mut self, key: &K) -> Option<&'a str>
    where
        K: Hash + Eq,
    {
        // ...
    }
}

我必须多次打电话:

fn new() -> Result<Boo, String> {
    let item1 = match section.get("item1") {
        None => return Result::Err("no item1".to_string()),
        Some(v) => v,
    };
    let item2 = match section.get("item2") {
        None => return Result::Err("no item2".to_string()),
        Some(v) => v,
    };
}
为了减少代码臃肿,我可以编写这样的宏:

为了减少代码冗余,我可以编写以下宏:

macro_rules! try_ini_get {
    ($e:expr) => {
        match $e {
            Some(s) => Ok(s),
            None => Err("no ini item".to_string()),
        }
    }
}

有没有办法在不使用这个宏的情况下消除代码重复?

2个回答

178
ok_orok_or_else方法将Option转换为Result,而?运算符自动处理与早期Err返回相关的样板文件。

你可以这样做:

fn new() -> Result<Boo, String> {
    let item1 = section.get("item1").ok_or("no item1")?;
    let item2 = section.get("item2").ok_or("no item2")?;
    // whatever processing...
    Ok(final_result)
}

12
ok_orok_or_else的区别:传递给ok_or的参数是急切地被求值的;如果您正在传递函数调用的结果,建议使用ok_or_else,因为它是惰性求值的。 - MakotoE
@MakotoE:当然。我只使用了ok_or(),因为传递的参数是一个常量。 - isekaijin

41
如果您正在使用箱子anyhow,则可以导入 anyhow::Context 特质,它将在Option上添加.context方法以将它们转换为 anyhow::Result
use anyhow::{Result, Context};

fn new() -> Result<Boo> {
    let item1 = section.get("item1").context("no item1")?;
    let item2 = section.get("item2").context("no item2")?;
    // whatever processing...
    Ok(final_result)
}

1
这对我解决了将Option转换为Result的问题,如果Option为None。现在,在返回Result<(), Box<dyn Error>>的函数中,我可以将None作为传播错误条件。 - Bots Fab
我已经有一段时间没有使用 Rust 了。如果这个答案反映了当前的最佳实践,那么应该将其标记为被接受的,而不是我的答案。 - isekaijin
2
在我看来,一个简单的无依赖解决方案总是最佳实践,被接受的答案很好。 - joseLuís
1
@joseLuís 对的,但如果你已经依赖于anyhow,这是一个非常好的选择。 - Joshua Davis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接