【问题标题】:Should I ditch using `and_then` and always use the `?` operator?我是否应该放弃使用 `and_then` 并始终使用 `?` 运算符?
【发布时间】:2021-07-26 10:28:49
【问题描述】:

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

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 看起来很慢并且“锅炉板”,所以我将其替换为:

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 并始终使用? 运算符吗?

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

【问题讨论】:

  • 您可以随时将其拆分为多个let val 语句:let val = env::args().nth (1).ok_or ("…")?; let val = File::open (val)?; let val = serde_yaml::from_reader (val)?;
  • 或者使用像map_forcompmdo这样的板条箱。免责声明:我写了map_for

标签: error-handling rust rust-result


【解决方案1】:

我应该放弃 and_then 并始终使用 吗?运营商?

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

有没有办法让结果组合器像 ?运营商?

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

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

【讨论】:

    【解决方案2】:

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

    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&lt;T&gt;。如果您查看ok_or 签名,即pub fn ok_or&lt;E&gt;(self, err: E) -&gt; Result&lt;T, E&gt;。这意味着对于您的情况,ok_or("1 arg is expected") 您的返回类型是 Result&lt;T, &amp;str&gt;。所以这里的错误类型是&amp;str,因为在 ok_or 中,您将字符串切片作为错误类型传递。

    如果你看一下and_then方法,签名是pub fn and_then&lt;U, F&gt;(self, op: F) -&gt; Result&lt;U, E&gt; where F: FnOnce(T) -&gt; Result&lt;U, E&gt;,基本上这意味着你传入的Function and_then应该和原始Result有相同的错误类型,即&amp;str。这与.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(())
    }
    

    【讨论】:

    • OP 已经知道这一点:“在每个闭包中添加 map_err 似乎很慢而且‘锅炉板’”
    猜你喜欢
    • 2010-09-08
    • 2011-04-06
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    • 2012-06-17
    • 1970-01-01
    相关资源
    最近更新 更多