首页 > 解决方案 > 如何从文件向量创建单个流?

问题描述

我正在尝试读取多个文件,我想从文件路径向量创建一个流。我一直在与编译器斗争一段时间,但我不确定如何使这项工作:

fn formatted_tags_stream(
    args: &[&str],
    files: &Vec<PathBuf>,
) -> Result<impl Iterator<Item = String>> {
    
    // Transform list of paths into a list of files
    let files: Vec<File> = files.into_iter().map(|f| File::open(f)).flatten().collect();

    // Here is where I'm stuck :/
    let stream =
        files
            .into_iter()
            .skip(1)
            .fold(BufReader::new(files[0]), |acc, mut f| acc.chain(f));

    Ok(BufReader::new(stream).lines().filter_map(|line| {
        line.ok().and_then(|tag| {
            if let Ok(tag) = serde_json::from_str::<TagInfo>(&tag) {
                Some(tag.format())
            } else {
                None
            }
        })
    }))
}

标签: rust

解决方案


如果使用fold,则函数必须始终返回相同的类型。但是,如果您在编写时使用它,该函数将采用一个类型并在第一遍中F返回 a 。Chain<F, ...>下一次传递需要采用 aChain<F, ...>并返回 a Chain<Chain<F, ...>, ...>- 导致“每次迭代不同类型”。这是行不通的,因为 Rust 想知道确切的类型,并且类型必须保持不变。

但是,您可以对事物进行类型擦除并将其隐藏在指针后面(即Box,点“特征对象”)。见这里(我做了一些小的调整以使其编译)

use std::path::PathBuf;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::io::BufRead;

fn formatted_tags_stream(
    args: &[&str],
    files: &Vec<PathBuf>,
) -> Result<impl Iterator<Item = String>, ()> {
    
    // Transform list of paths into a list of files
    let files: Vec<File> = files.into_iter().map(|f| File::open(f)).flatten().collect();

    // Here is where I'm stuck :/
    let bufreader = Box::new(std::io::empty()) as Box<dyn Read>;
    let stream =
        files
            .into_iter()
            .fold(bufreader, |acc, f| Box::new(acc.chain(f)) as Box<dyn Read>);

    Ok(BufReader::new(stream).lines().filter_map(|line| {
        line.ok().and_then(|tag| {
            if let Ok(_tag) = tag.parse::<usize>() {
                Some(tag)
            } else {
                None
            }
        })
    }))
}

请注意,使用 aBox<dyn Read>会产生一些运行时开销,因为它会导致动态调度。


推荐阅读