在学习Rust的过程中,我熟悉了错误传播以及unwrap?运算符之间的选择。在编写了仅使用unwrap()的原型(prototype)代码之后,我想从可重用的部分中删除unwrap,在这种情况下,对每个错误都感到 panic 是不合适的。

像本例一样,如何避免在闭包中使用unwrap

// todo is VecDeque<PathBuf>
let dir = fs::read_dir(&filename).unwrap();
todo.extend(dir.map(|dirent| dirent.unwrap().path()));

只要包含函数返回unwrap或类似内容,第一个?即可轻松更改为Result<(), io::Error>。但是,第二个unwrap(即dirent.unwrap().path()中的一个)不能更改为dirent?.path(),因为闭包必须返回PathBuf而不是Result<PathBuf, io::Error>

一种选择是将extend更改为显式循环:
let dir = fs::read_dir(&filename)?;
for dirent in dir {
    todo.push_back(dirent?.path());
}

但这感觉是错误的-原始的extend很优雅并且清楚地反射(reflect)了代码的意图。 (它可能比一系列push_back更有效。)有经验的Rust开发人员将如何在此类代码中表达错误检查?

最佳答案



好吧,这实际上取决于您要对失败执行的操作。

  • 应该向用户报告失败或保持沉默
  • 如果报告了,应该报告一个失败还是全部报告?
  • 如果发生故障,是否应该中断处理?

  • 例如,您可以完美地决定静默地忽略所有失败,而只跳过失败的条目。在这种情况下, Iterator::filter_map Result::ok 组合正是您要的。
    let dir = fs::read_dir(&filename)?;
    let todos.extend(dir.filter_map(Result::ok));
    
    Iterator接口(interface)包含很多东西,在查找整理代码时绝对值得细读。

    关于error-handling - 适用于迭代器映射的try(?)运算符的替代方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40322300/

    10-13 05:08