替代适合迭代器映射的try(?)运算符

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了替代适合迭代器映射的try(?)运算符相关的知识,希望对你有一定的参考价值。

在学习Rust的过程中,我熟悉了错误传播以及unwrap?运算符之间的选择。在编写了一些仅使用unwrap()的原型代码之后,我想从可重复使用的部分中删除unwrap,其中对每个错误的恐慌是不合适的。

如何避免在闭包中使用unwrap,就像在这个例子中一样?

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

第一个unwrap可以很容易地改为?,只要包含函数返回Result<(), io::Error>或类似。然而,第二个unwrapdirent.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很优雅,清楚地反映了代码的意图。 (它可能比push_backs序列更有效。)经验丰富的Rust开发人员如何在这些代码中表达错误检查?

答案

如何避免在闭包中使用unwrap,就像在这个例子中一样?

嗯,这真的取决于你在失败时想做什么。

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

例如,您可以完美地决定以静默方式忽略所有故障,并跳过失败的条目。在这种情况下,Iterator::filter_mapResult::ok结合正是你所要求的。

let dir = fs::read_dir(&filename)?;
let todos.extend(dir.filter_map(Result::ok));

Iterator界面充满了好东西,在寻找更整洁的代码时绝对值得细读。

另一答案

这是一个基于filter_map建议的Matthieu的解决方案。它调用Result::map_err以确保错误被​​“捕获”并记录,将其发送到Result::okfilter_map以从迭代中删除它:

fn log_error(e: io::Error) {
    writeln!(&mut std::io::stderr(), "{}", e).unwrap()
}

(|| {
    let dir = fs::read_dir(&filename)?;
    todo.extend(dir
                .filter_map(|res| res.map_err(log_error).ok()))
                .map(|dirent| dirent.path()));
})().unwrap_or_else(log_error)

以上是关于替代适合迭代器映射的try(?)运算符的主要内容,如果未能解决你的问题,请参考以下文章

std::map 上可能的线程不安全操作

递归,三目运算,匿名函数,迭代器

片段着色器中的球面映射

对变量名使用迭代器的替代方法

指向对象的指针映射上的迭代器作为关键 C++

OpenMP 无法使用映射迭代器并行化 for 循环