我想使用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
箱子或是否有其他方法?
答案 0 :(得分:5)
这是 zip
crate的API 的限制,您无法真正改变任何内容。问题是文件ZipArchive
是通过调用new
并传递读者来创建的 - 实现Read
和Seek
的内容。但这些是读者的唯一要求(特别是,读者不需要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可能会满足您的需求。