【问题标题】:What is the idiomatic way to handle/unwrap nested Result types?处理/解包嵌套 Result 类型的惯用方法是什么?
【发布时间】:2016-10-01 11:06:12
【问题描述】:

我读到在 Result 上使用 unwrap 在 Rust 中不是一个好的做法,最好使用模式匹配,以便可以适当地处理发生的任何错误。

我明白了,但是考虑一下这个读取目录并打印每个条目的访问时间的 sn-p:

use std::fs;
use std::path::Path;

fn main() {
    let path = Path::new(".");
    match fs::read_dir(&path) {
        Ok(entries) => {
            for entry in entries {
                match entry {
                    Ok(ent) => {
                        match ent.metadata() {
                            Ok(meta) => {
                                match meta.accessed() {
                                    Ok(time) => {
                                        println!("{:?}", time);
                                    },
                                    Err(_) => panic!("will be handled")
                                }
                            },
                            Err(_) => panic!("will be handled")
                        }
                    },
                    Err(_) => panic!("will be handled")
                }
            }
        },
        Err(_) => panic!("will be handled")
    }
}

我想处理上面代码中所有可能的错误(panic 宏只是一个占位符)。虽然上面的代码有效,但我认为它很难看。处理这种情况的惯用方法是什么?

【问题讨论】:

    标签: coding-style rust


    【解决方案1】:

    我读到在 Result 上使用 unwrap 在 Rust 中不是一个好习惯。

    这并不容易。例如,阅读my answer here 以了解更多信息。现在解决您的主要问题:


    通过将Ok 值传递给外部来减少右移

    代码的一个大问题是右移:例如,meta.accessed() 调用缩进了很多。我们可以通过在match 之外传递我们想要使用的值来避免这种情况:

    let entries = match fs::read_dir(&path) {
        Ok(entries) => entries, // "return" from match
        Err(_) => panic!("will be handled"),
    };
    
    for entry in entries {  // no indentation! :)
        // ...
    }
    

    这已经是让代码更具可读性的好方法了。

    使用? 运算符将错误传递给调用函数

    您的函数可以返回 Result<_, _> 类型,以便将错误传递给调用函数(是的,even main() can return Result)。在这种情况下,您可以使用? 运算符:

    use std::{fs, io};
    
    fn main() -> io::Result<()> {
        for entry in fs::read_dir(".")? {
            println!("{:?}", entry?.metadata()?.accessed()?);
        }
        Ok(())
    }
    

    使用Result的辅助方法

    对于Result 类型,还有许多辅助方法,例如map()and_then()。如果你想做某事,and_then 会很有帮助,如果结果是 Ok 并且这个东西会返回相同类型的结果。这是带有and_then() 和手动处理错误的代码:

    fn main() {
        let path = Path::new(".");
        let result = fs::read_dir(&path).and_then(|entries| {
            for entry in entries {
                let time = entry?.metadata()?.accessed()?;
                println!("{:?}", time);
            }
            Ok(())
        });
    
        if let Err(e) = result {
            panic!("will be handled");
        }
    }
    

    确实不只有一种方法可以进行这种错误处理。您必须了解所有可以使用的工具,然后需要选择最适合您的情况的工具。但是,在大多数情况下,? 运算符是正确的工具。

    【讨论】:

    • 另一个很好的来源是 BurntSushi 的 "Error Handling in Rust" 文章。
    • and_then(|_| { foo; Ok(()) }); 可能只是 map(|_| { foo; });
    • @mcarton 谢谢!我简化了代码以避免这个问题。
    【解决方案2】:

    Result happens to have a lot of convenience methods 用于此类事情:

    use std::fs;
    use std::path::Path;
    
    fn main() {
        let path = Path::new(".");
        match fs::read_dir(&path) {
            Ok(entries) => {
                for entry in entries {
                    match entry.and_then(|e| e.metadata()).map(|m| m.accessed()) {
                        Ok(time) => {
                            println!("{:?}", time);
                        },
                        Err(_) => panic!("will be handled")
                    }
                }
            },
            Err(_) => panic!("will be handled")
        }
    }
    

    而且通常你不会在main 中使用太多逻辑,并且可以简单地在另一个函数中使用?try!

    use std::fs;
    use std::path::Path;
    
    fn print_filetimes(path: &Path) -> Result<(), std::io::Error> {
        for entry in fs::read_dir(&path)? {
            let time = entry.and_then(|e| e.metadata()).map(|m| m.accessed())?;
            println!("{:?}", time);
        }
    
        Ok(())
    }
    
    fn main() {
        let path = Path::new(".");
        match print_filetimes(path) {
            Ok(()) => (),
            Err(_) => panic!("will be handled"),
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-28
      • 2021-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多