1

假设我想读取目录中的所有文件。我可能会定义一个这样的函数:

use std::error::Error;
use std::fs;
use std::io;

type Result<T> = std::result::Result<T, Box<dyn Error>>;

fn read_entry(entry: io::Result<fs::DirEntry>) -> Result<Option<String>> {
    let entry = entry?;
    if entry.file_type()?.is_file() {
        Ok(Some(fs::read_to_string(entry.file_name())?))
    } else {
        Ok(None)
    }
}

然后尝试使用它...

use std::path::Path;

fn read_all(dir: &Path) -> Result<Vec<String>> {
    Ok(fs::read_dir(dir)?
        .filter_map(|entry| read_entry(entry).unwrap())
        .collect())
}

这可以编译,但它只是unwrap()错误。collect()通常可以在错误上聚合迭代器,但我不太清楚如何使用filter_map(). 我怎样才能解决这个问题?

操场

4

3 回答 3

4

有一个很好的for实现(这是 for 的一个潜在特征),所以这很有效:FromIteratorIter<Item = Result>Iterator::collect

fn read_all(dir: &Path) -> Result<Vec<String>> {
    fs::read_dir(dir)?
        .filter_map(|entry| read_entry(entry).transpose())
        .collect()
}
于 2020-06-26T16:56:11.790 回答
1

而不是返回Result<Option<T>>你需要Option<Result<T>>,即之前:

  • Ok(Some(T)):这是一个文件,我们成功读取了它
  • Ok(None): 这不是一个文件,但我们没有发现任何错误。
  • Err: 有一个错误。

后:

  • Some(Ok(T)):这是一个文件,我们成功读取了它
  • None: 这不是一个文件,但我们没有发现任何错误。
  • Some(Err): 有一个错误。

您可以更改原始实现,但这意味着您不能使用?运算符,这很糟糕。

一个更好的解决方案 - 正如@Kitsu 指出的那样,使用内置函数transpose()将 aResult<Option<T>>转换为Option<Result<T>>.

于 2020-06-26T17:01:03.513 回答
0

只是为了完成,read_entry失败的原因是因为使用entry.file_name()而不是entry.path().

所以这:

Ok(Some(fs::read_to_string(entry.file_name())?))

应该:

Ok(Some(fs::read_to_string(entry.path())?))
于 2020-06-26T17:05:39.107 回答