处理/解包嵌套Result类型的惯用方法是什么?

时间:2016-10-01 11:06:13

标签: coding-style rust

我读到在unwrap上使用Result在Rust中不是一个好习惯,并且最好使用模式匹配,以便可以正确处理发生的任何错误。

我明白了这一点,但考虑一下该片段读取目录并打印每个条目的访问时间:

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宏只是一个占位符)。虽然上面的代码有效,但我觉得它很难看。处理这种情况的惯用方法是什么?

2 个答案:

答案 0 :(得分:11)

  

我读到在结果中使用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");
    }
}

确实不仅有一种方式来执行此类错误处理。您必须了解可以使用的所有工具,然后需要根据您的具体情况选择最佳工具。但是,在大多数情况下,?运算符是正确的工具。

答案 1 :(得分:4)

Result happens to have a lot of convenience methods对于这些事情:

ng-bind-html

通常你在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") } } 中没有那么多逻辑,只能在另一个函数中使用maintry!

?