我可以将 Result 的 Iterator 转换为 Iterator 的 Result 吗?

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

到目前为止,我已经使用

std::fs::read_to_string
,然后使用
String.lines
std::str::Lines
(这是一个
Iterator<Item = &str>
)来“逐行”读取文件。这显然将整个文件读入内存,这并不理想。

因此,有

BufRead.lines()
可以真正逐行读取文件。这将返回
std::io::Lines
(这是一个
Iterator<Item = Result<String>>
)。

如何在不先

collect
的情况下从一种迭代器类型转换为另一种迭代器类型?

rust iterator
3个回答
1
投票

您无法将

Iterator<Item = Result<_, _>>
转换为
Result<Iterator<Item = _>, _>
,因为如果我们还没有迭代迭代器,我们不知道是否会产生错误。

你可以做的就是提前将所有项目

collect()
放入
Result<Vec<_>, _>
(当然你可以迭代),因为
Result
实现了
FromIterator

如果您同意仅对第一个

Err
获取
Err
(并成功迭代所有项目直到该结果),您也可以使用
itertools::process_results()
:

let result: Result<SomeType, _> = itertools::process_results(iter, |iter| -> SomeType {
    // Here we have `iter` of type `Iterator<Item = _>`. Process it and return some result.
});

0
投票

避免集合是有意义的,因为这意味着运行整个迭代器一次只是为了解压所有

Result

我怀疑您真正想要解决的问题是如何映射/过滤

Result<String, _>
的迭代器,而不在每行调用
unwrap
。这里的方法不是将
Iter<Result<T, E>>
转换为
Result<Iter<T>, E>
,而是在每个映射/过滤器中解包
Result
类型,然后将输出重新打包到 Result 中,以将任何错误推送到下一步。

这是一个通用示例:

use std::{
    fs::File,
    io::{BufReader, BufRead},
}

fn parse_line(input: String) -> usize {
    // .. Dummy code that works on an input line
    todo!()
}

fn parse_lines() {
    let lines: Lines<BufReader<File>> = BufReader::new(
        File::open("my_file.txt").unwrap()
    ).lines();

    let new_iter: Iter< = lines.map(|line: Result<String, Error>| {
        // We can't pass a `Result` into our `parse_line` function,
        // so we unpack it first.
        match line {
            // If no error, do work with the contents of the `Ok` val.
            Ok(s) => Ok(parse_line(s)),
            // We don't want to do any destructive error handling 
            // prematurely, so we pass any errors back up the chain.
            Err(e) => Err(e)
        }
    };
}

请注意,几乎在每个实例中,您仍然希望在映射/过滤器链的末尾进行某种错误处理。这通常是通过将迭代器转换为

collect
类型来完成的,例如
Result
,就像 Chayim 提到的那样。但我演示的方法避免了多次调用
Result<Vec<_>>
    


-1
投票
collect

的情况下,该值是完整的

String
。 但是,您可以将
String.lines
转换为
Iterator<Item = Result<String>
上的迭代器:
Strings

您可以在 
let mut read = BufReader::new(File::open("src/main.rs").unwrap()); let lines_iter = read.lines().map(Result::unwrap_or_default);

Iterator
的项目上加上
String
,如下所示:
&str

© www.soinside.com 2019 - 2024. All rights reserved.