首页 > 解决方案 > Reading ZIP file in Rust causes data owned by the current function

问题描述

I'm new to Rust and am likely have a huge knowledge gap. Basically, I'm hoping to be create a utility function that would except a regular text file or a ZIP file and return a BufRead where the caller can start processing line by line. It is working well for non ZIP files but I am not understanding how to achieve the same for the ZIP files. The ZIP files will only contain a single file within the archive which is why I'm only processing the first file in the ZipArchive.

I'm running into the the following error.

error[E0515]: cannot return value referencing local variable `archive_contents`
  --> src/file_reader.rs:30:9
   |
27 |         let archive_file: zip::read::ZipFile = archive_contents.by_index(0).unwrap();
   |                                                ---------------- `archive_contents` is borrowed here
...
30 |         Ok(Box::new(BufReader::with_capacity(128 * 1024, archive_file)))
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

It seems the archive_contents is preventing the BufRead object from returning to the caller. I'm just not sure how to work around this.

file_reader.rs

use std::ffi::OsStr;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;

pub struct FileReader {
    pub file_reader: Result<Box<BufRead>, &'static str>,
}

pub fn file_reader(filename: &str) -> Result<Box<BufRead>, &'static str> {
    let path = Path::new(filename);
    let file = match File::open(&path) {
        Ok(file) => file,
        Err(why) => panic!(
            "ERROR: Could not open file, {}: {}",
            path.display(),
            why.to_string()
        ),
    };

    if path.extension() == Some(OsStr::new("zip")) {
        // Processing ZIP file.
        let mut archive_contents: zip::read::ZipArchive<std::fs::File> =
            zip::ZipArchive::new(file).unwrap();

        let archive_file: zip::read::ZipFile = archive_contents.by_index(0).unwrap();

        // ERRORS: returns a value referencing data owned by the current function
        Ok(Box::new(BufReader::with_capacity(128 * 1024, archive_file)))
    } else {
        // Processing non-ZIP file.
        Ok(Box::new(BufReader::with_capacity(128 * 1024, file)))
    }
}

main.rs

mod file_reader;

use std::io::BufRead;

fn main() {
    let mut files: Vec<String> = Vec::new();

    files.push("/tmp/text_file.txt".to_string());
    files.push("/tmp/zip_file.zip".to_string());

    for f in files {
        let mut fr = match file_reader::file_reader(&f) {
            Ok(fr) => fr,
            Err(e) => panic!("Error reading file."),
        };

        fr.lines().for_each(|l| match l {
            Ok(l) => {
                println!("{}", l);
            }
            Err(e) => {
                println!("ERROR: Failed to read line:\n  {}", e);
            }
        });
    }
}

Any help is greatly appreciated!

标签: rust

解决方案


似乎 archive_contents 正在阻止 BufRead 对象返回给调用者。我只是不确定如何解决这个问题。

您必须以某种方式重组代码。这里的问题是,档案数据是档案的一部分。所以不像file,archive_file不是一个独立的项目,而是一个指向档案本身的排序指针。这意味着存档需要比archive_file此代码正确的寿命更长。

在 GC 语言中,这不是问题,archive_file有一个参考,archive并且无论它需要多久,它都会保持活力。对于 Rust 来说并非如此。

解决此问题的一种简单方法是将数据复制出archive_file并复制到一个拥有的缓冲区中,您可以将其返回给父级。另一种选择可能是返回一个包装器(archive_contents, item_index),这将委托阅读(虽然可能有点棘手)。还有一个是没有file_reader


推荐阅读