1

我想写这样的东西,但由于类型之间的不匹配而无法编译:

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let val = std::env::args()
        .nth(1)
        .ok_or("1 arg is expected")
        .and_then(std::fs::File::open)
        .and_then(serde_yaml::from_reader)?;
}

因为map_err在每个闭包中添加 a 看起来很慢并且“锅炉板”,所以我将其替换为以下内容:

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let val = serde_yaml::from_reader(std::fs::File::open(
        std::env::args().nth(1).ok_or("1 arg is expected")?,
    )?)?;
}

第一个感觉更自然,读起来像英语,而第二个感觉有点倒退。

我应该放弃and_then并始终使用?操作员吗?

如果没有,有没有办法让结果组合器像?运算符一样平滑?

4

2 回答 2

1

我应该放弃 and_then 并始终使用 吗?操作员 ?

这是个人判断,只有你能回答。

有没有办法让结果组合器像 ? 操作员 ?

坦白说没有。?“隐式”执行转换(不是真正隐式,因为它是其工作的很大一部分,但不必单独调用转换,也许“简洁”?),and_then没有。这意味着在使用时and_then,您必须自己执行这些转换。这似乎合乎逻辑。

您也许可以为此构建一个方便的宏。或者可能添加一个扩展方法或包装器类型,可以在后台执行这些转换。

于 2021-05-04T06:29:22.013 回答
0

您的第一个表达式不起作用的原因是错误类型不匹配。更加具体,

let val = std::env::args()
        .nth(1)
        .ok_or("1 arg is expected")
        .and_then(std::fs::File::open)
        .and_then(serde_yaml::from_reader)?;

std::env::args().nth(1)返回一个Option<T>。如果您查看ok_or签名,即pub fn ok_or<E>(self, err: E) -> Result<T, E>. 这意味着对于您的情况,ok_or("1 arg is expected")您的返回类型是Result<T, &str>. 所以这里的错误类型是&str因为在 ok_or 中,您将字符串切片作为错误类型传递。

如果你看一下and_then方法,签名是pub fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>,基本上这意味着你传入的Function and_then 应该与原始Result具有相同的错误类型,即a &str。这与 line 相同.and_then(serde_yaml::from_reader),所有“链接”和_then 函数的错误类型需要一致。

如果你真的想要,你可以通过以下方式来编译你的代码。或者您可以创建一个统一的错误,这样就不会出现不匹配的错误类型。然后你就可以用ok_or(...).and_then(...).and_then(...)那种写法了。您只需要匹配错误类型。

工作示例

fn custom_fs_open(path: String) -> Result<String, &'static str>{
    // actual logics to open fs
    Ok(String::from("abc"))
}


fn custom_serde_yaml(buf: String) -> Result<String, &'static str>{
    // actual logics to do serde convertion
    Ok(String::from("cde"))
}


fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let val = std::env::args()
            .nth(1)
            .ok_or("1 arg is expected")
            .and_then(custom_fs_open)
            .and_then(custom_serde_yaml)?;
            
    Ok(())
}
于 2021-05-04T12:54:25.597 回答