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

问题描述 投票:0回答:2

在学习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开发人员如何在这些代码中表达错误检查?

error-handling rust
2个回答
3
投票

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

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

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

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

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

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


1
投票

这是一个基于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)
© www.soinside.com 2019 - 2024. All rights reserved.