Rust中如何将Result的迭代器转换为迭代器的Result?
问题
之前我用std::fs::read_to_string配合String.lines()返回的std::str::Lines(一种Iterator<Item=&str>类型)实现文件“逐行”读取,但这种方式会把整个文件加载到内存里,不太理想。而BufRead.lines()能实现真正的逐行读取,它返回的std::io::Lines是Iterator<Item=Result<String>>类型。请问怎么在不调用collect方法的前提下,把这种Result的迭代器转换成迭代器的Result?
解决方案
由于迭代器是惰性求值的特性,你无法在不遍历元素的前提下提前确认所有行是否都读取成功。以下是两种贴合需求的实现方式:
方式一:用try_fold替代collect收集结果
try_fold会在遇到第一个错误时立即终止遍历并返回错误,既避免了collect的显式调用,也能一次性拿到所有行的结果:
use std::fs::File; use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { let file = File::open("example.txt")?; let reader = BufReader::new(file); // 将所有行收集为 Result<Vec<String>, io::Error> let lines_result = reader.lines().try_fold(Vec::new(), |mut acc, line| { acc.push(line?); Ok(acc) }); match lines_result { Ok(lines) => lines.iter().for_each(|line| println!("{}", line)), Err(e) => eprintln!("读取错误: {}", e), } Ok(()) }
方式二:惰性遍历中逐行处理错误
如果要严格保持逐行读取的惰性,直接在遍历过程中处理每个Result即可,遇到错误时立即终止并返回:
use std::fs::File; use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { let file = File::open("example.txt")?; let reader = BufReader::new(file); // 逐行遍历,错误直接向上传播 for line_result in reader.lines() { let line = line_result?; println!("{}", line); } Ok(()) }
补充:包装为迭代器的Result(有限场景)
如果确实需要将迭代器整体包装为Result<impl Iterator<Item=String>, io::Error>,可以先检查第一行是否有错误,再组合剩余迭代器(但后续行出错仍会触发panic,需谨慎使用):
use std::fs::File; use std::io::{self, BufRead, BufReader}; fn lines_to_result_iter(mut lines: impl Iterator<Item=io::Result<String>>) -> io::Result<impl Iterator<Item=String>> { // 先检查第一行是否存在错误 let first_line = match lines.next() { Some(Ok(line)) => Some(line), Some(Err(e)) => return Err(e), None => None, }; // 组合第一行和剩余迭代器,转换为仅返回String的迭代器 let iter = first_line.into_iter().chain(lines.map(|res| res.unwrap())); Ok(iter) } fn main() -> io::Result<()> { let file = File::open("example.txt")?; let reader = BufReader::new(file); let result_iter = lines_to_result_iter(reader.lines())?; result_iter.for_each(|line| println!("{}", line)); Ok(()) }
内容的提问来源于stack exchange,提问作者xehpuk
相关产品推荐
相关产品推荐

