有没有办法实现ZipFile的发送特征?

时间:2017-08-15 10:21:36

标签: rust zip

我想使用zip crate在不同的主题中阅读.zip文件。

extern crate zip;

use zip::ZipArchive;
use zip::read::ZipFile;
use std::fs::File;
use std::io::BufReader;
use std::thread;

fn compute_hashes(mut file: ZipFile) {
    let reader_thread= thread::spawn(move || {
        let mut reader = BufReader::new(file);
        /* ... */
    });
}

fn main() {
    let mut file = File::open(r"facebook-JakubOnderka.zip").unwrap();
    let mut zip = ZipArchive::new(file).unwrap();

    for i in 0..zip.len() {
        let mut inside = zip.by_index(i).unwrap();

        if !inside.name().ends_with("/") { // skip directories
            println!("Filename: {}", inside.name());
            compute_hashes(inside);
        }
    }
}

但是编译器向我显示了这个错误:

error[E0277]: the trait bound `std::io::Read: std::marker::Send` is not satisfied
  --> src/main.rs:10:24
   |
10 |     let reader_thread= thread::spawn(move || {
   |                        ^^^^^^^^^^^^^ `std::io::Read` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `std::io::Read`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::io::Read`
   = note: required because it appears within the type `std::io::Take<&mut std::io::Read>`
   = note: required because it appears within the type `zip::crc32::Crc32Reader<std::io::Take<&mut std::io::Read>>`
   = note: required because it appears within the type `zip::read::ZipFileReader<'_>`
   = note: required because it appears within the type `zip::read::ZipFile<'_>`
   = note: required because it appears within the type `[closure@src/main.rs:10:38: 13:6 file:zip::read::ZipFile<'_>]`
   = note: required by `std::thread::spawn`

但同样适用于std::fs::File类型。是否有必要修复zip箱子或是否有其他方法?

1 个答案:

答案 0 :(得分:5)

这是 zip crate的API 的限制,您无法真正改变任何内容。问题是文件ZipArchive是通过调用new并传递读者来创建的 - 实现ReadSeek的内容。但这些是读者的唯一要求(特别是,读者不需要Clone)。因此,整个ZipArchive只能拥有一个读者。

但现在ZipArchive能够生成自己实现ZipFile的{​​{1}}。如果整个Read只有一个读者,那该怎么办?通过分享!归档和所有文件之间共享唯一的读者。但这个共享不是线程保存!每个ZipArchive都存储了对读者的可变引用 - 这违反了Rust的核心原则。

这是一个已知的箱子问题,正在讨论on the GitHub issue tracker

那你现在能做什么?不是很多,但是一些可能性(如图书馆作者所提到的)可能适用于您的用例:

  • 您可以先将整个文件解压缩到内存中,然后将原始数据发送到另一个线程以对其进行计算。类似的东西:

    ZipFile

    但是如果你只是想在所有文件上计算一个廉价的哈希函数,那么将内容加载到内存中可能比在运行中计算哈希值要慢,如果你的文件很大,可能是不可行的。

  • 为每个帖子创建一个let data = Vec::new(); BufReader::new(file).read_to_end(&mut data)?; let reader_thread= thread::spawn(move || { // Do stuff with `data` }); 。如果您的档案中有许多小文件,这可能会非常慢......

一个微小的提示:开始一个线程需要时间。您通常不希望为每个工作单元启动一个线程,而是在线程池中维护固定数量的线程,管理队列中的工作并将工作分配给空闲工作线程。 The threadpool crate可能会满足您的需求。