This is a follow up of my previous post. Now I'm trying to read an entry which is a file from an archive and want to write the contents of that to destination file. Also, I want to print the progress of file write operation.
use std::fs::{File, metadata};
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Write};
use std::ffi::OsStr;
use std::thread;
use tar::{Archive, Entry};
use flate2::read::GzDecoder;
enum ArchiveType {
Tar,
TarGz,
}
fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
match archive_type {
ArchiveType::Tar => Archive::new(Box::new(file)),
ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
}
}
fn get_entry<'a, R: Read>(
archive: &'a mut Archive<R>,
entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
let normalized_entry_name = entry_name.trim_start_matches("./");
for entry in archive.entries()? {
let entry = entry?;
// comparison between 'entry_name' and path of 'entry'
// is done only with file_name() we ignore subfolders.
// if multiple entries with same name, only first will be found.
if entry
.path()
.as_ref()
.ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
.and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
.and_then(OsStr::to_str) // Convert to Option<&str> safely
.map(|name| name == normalized_entry_name) // Compare
.unwrap_or(false)
{
return Ok(entry);
}
}
Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}
fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
let mut buf = vec![0; 1024];
let mut bytes_read = buf.capacity();
while bytes_read > 0 {
bytes_read = reader.read(&mut buf).expect("failed to read from the file");
if bytes_read > 0 {
writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
}
}
writer.flush().expect("failed to flush the writer");
}
fn main() {
thread::scope(|ts| {
let mut archive =
get_archive(ArchiveType::Tar, Path::new("abc.tar"));
let file_name = "def.txt".to_string();
let src_file = get_entry(&mut archive, &file_name).expect("error while getting entry");
let metadata = metadata(src_file.path().unwrap()).unwrap();
let dst_file = &File::create("b.txt").expect("unable to create the file");
let sjh = ts.spawn(move || {
write_to_file(src_file, dst_file);
});
while !sjh.is_finished() {
let progress = src_file.raw_file_position();
{
println!("{}", 100 * progress / metadata.len());
}
}
});
}
Error
error[E0277]: `Cell<u64>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `Cell<u64>` cannot be shared between threads safely
|
= help: within `Archive<Box<(dyn std::io::Read + 'static)>>`, the trait `std::marker::Sync` is not implemented for `Cell<u64>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead
note: required because it appears within the type `tar::archive::ArchiveInner<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
note: required because it appears within the type `Archive<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:20:12
|
20 | pub struct Archive<R: ?Sized + Read> {
| ^^^^^^^
= note: required for `&Archive<Box<(dyn std::io::Read + 'static)>>` to implement `Send`
note: required because it appears within the type `PhantomData<&Archive<Box<(dyn std::io::Read + 'static)>>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
error[E0277]: `RefCell<Box<(dyn std::io::Read + 'static)>>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `RefCell<Box<(dyn std::io::Read + 'static)>>` cannot be shared between threads safely
|
= help: within `Archive<Box<(dyn std::io::Read + 'static)>>`, the trait `std::marker::Sync` is not implemented for `RefCell<Box<(dyn std::io::Read + 'static)>>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required because it appears within the type `tar::archive::ArchiveInner<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
note: required because it appears within the type `Archive<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:20:12
|
20 | pub struct Archive<R: ?Sized + Read> {
| ^^^^^^^
= note: required for `&Archive<Box<(dyn std::io::Read + 'static)>>` to implement `Send`
note: required because it appears within the type `PhantomData<&Archive<Box<(dyn std::io::Read + 'static)>>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
error[E0277]: `RefCell<dyn std::io::Read>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `RefCell<dyn std::io::Read>` cannot be shared between threads safely
|
= help: within `tar::archive::ArchiveInner<dyn std::io::Read>`, the trait `std::marker::Sync` is not implemented for `RefCell<dyn std::io::Read>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required because it appears within the type `tar::archive::ArchiveInner<dyn std::io::Read>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
= note: required for `&tar::archive::ArchiveInner<dyn std::io::Read>` to implement `Send`
note: required because it appears within the type `std::io::Take<&tar::archive::ArchiveInner<dyn std::io::Read>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:2819:12
|
2819 | pub struct Take<T> {
| ^^^^
note: required because it appears within the type `tar::entry::EntryIo<'_>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:47:10
|
47 | pub enum EntryIo<'a> {
| ^^^^^^^
note: required because it appears within the type `PhantomData<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `alloc::raw_vec::RawVec<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:76:19
|
76 | pub(crate) struct RawVec<T, A: Allocator = Global> {
| ^^^^^^
note: required because it appears within the type `Vec<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:397:12
|
397 | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
| ^^^
note: required because it appears within the type `tar::entry::EntryFields<'_>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:30:12
|
30 | pub struct EntryFields<'a> {
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
For more information about this error, try `rustc --explain E0277`.
This is a follow up of my previous post. Now I'm trying to read an entry which is a file from an archive and want to write the contents of that to destination file. Also, I want to print the progress of file write operation.
use std::fs::{File, metadata};
use std::path::Path;
use std::io::{Error, ErrorKind, Read, Write};
use std::ffi::OsStr;
use std::thread;
use tar::{Archive, Entry};
use flate2::read::GzDecoder;
enum ArchiveType {
Tar,
TarGz,
}
fn get_archive(archive_type: ArchiveType, archive: &Path) -> Archive<Box<dyn Read>> {
let file = File::open(archive).expect(&format!("Failed to open archive {:?}", archive));
match archive_type {
ArchiveType::Tar => Archive::new(Box::new(file)),
ArchiveType::TarGz => Archive::new(Box::new(GzDecoder::new(file))),
}
}
fn get_entry<'a, R: Read>(
archive: &'a mut Archive<R>,
entry_name: &'a str,
) -> Result<Entry<'a, R>, Error> {
let normalized_entry_name = entry_name.trim_start_matches("./");
for entry in archive.entries()? {
let entry = entry?;
// comparison between 'entry_name' and path of 'entry'
// is done only with file_name() we ignore subfolders.
// if multiple entries with same name, only first will be found.
if entry
.path()
.as_ref()
.ok() // Convert Result to Option<Cow<Path>> (ignoring errors)
.and_then(|path| path.file_name()) // Extract file name (Option<&OsStr>)
.and_then(OsStr::to_str) // Convert to Option<&str> safely
.map(|name| name == normalized_entry_name) // Compare
.unwrap_or(false)
{
return Ok(entry);
}
}
Err(Error::new(ErrorKind::Other, "failed to get entry from the archive"))
}
fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W) {
let mut buf = vec![0; 1024];
let mut bytes_read = buf.capacity();
while bytes_read > 0 {
bytes_read = reader.read(&mut buf).expect("failed to read from the file");
if bytes_read > 0 {
writer.write_all(&buf[..bytes_read]).expect("failed to write to the file");
}
}
writer.flush().expect("failed to flush the writer");
}
fn main() {
thread::scope(|ts| {
let mut archive =
get_archive(ArchiveType::Tar, Path::new("abc.tar"));
let file_name = "def.txt".to_string();
let src_file = get_entry(&mut archive, &file_name).expect("error while getting entry");
let metadata = metadata(src_file.path().unwrap()).unwrap();
let dst_file = &File::create("b.txt").expect("unable to create the file");
let sjh = ts.spawn(move || {
write_to_file(src_file, dst_file);
});
while !sjh.is_finished() {
let progress = src_file.raw_file_position();
{
println!("{}", 100 * progress / metadata.len());
}
}
});
}
Error
error[E0277]: `Cell<u64>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `Cell<u64>` cannot be shared between threads safely
|
= help: within `Archive<Box<(dyn std::io::Read + 'static)>>`, the trait `std::marker::Sync` is not implemented for `Cell<u64>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead
note: required because it appears within the type `tar::archive::ArchiveInner<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
note: required because it appears within the type `Archive<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:20:12
|
20 | pub struct Archive<R: ?Sized + Read> {
| ^^^^^^^
= note: required for `&Archive<Box<(dyn std::io::Read + 'static)>>` to implement `Send`
note: required because it appears within the type `PhantomData<&Archive<Box<(dyn std::io::Read + 'static)>>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
error[E0277]: `RefCell<Box<(dyn std::io::Read + 'static)>>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `RefCell<Box<(dyn std::io::Read + 'static)>>` cannot be shared between threads safely
|
= help: within `Archive<Box<(dyn std::io::Read + 'static)>>`, the trait `std::marker::Sync` is not implemented for `RefCell<Box<(dyn std::io::Read + 'static)>>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required because it appears within the type `tar::archive::ArchiveInner<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
note: required because it appears within the type `Archive<Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:20:12
|
20 | pub struct Archive<R: ?Sized + Read> {
| ^^^^^^^
= note: required for `&Archive<Box<(dyn std::io::Read + 'static)>>` to implement `Send`
note: required because it appears within the type `PhantomData<&Archive<Box<(dyn std::io::Read + 'static)>>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
error[E0277]: `RefCell<dyn std::io::Read>` cannot be shared between threads safely
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ______________________-----_^
| | |
| | required by a bound introduced by this call
71 | | write_to_file(src_file, dst_file);
72 | | });
| |_________^ `RefCell<dyn std::io::Read>` cannot be shared between threads safely
|
= help: within `tar::archive::ArchiveInner<dyn std::io::Read>`, the trait `std::marker::Sync` is not implemented for `RefCell<dyn std::io::Read>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required because it appears within the type `tar::archive::ArchiveInner<dyn std::io::Read>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/archive.rs:24:12
|
24 | pub struct ArchiveInner<R: ?Sized> {
| ^^^^^^^^^^^^
= note: required for `&tar::archive::ArchiveInner<dyn std::io::Read>` to implement `Send`
note: required because it appears within the type `std::io::Take<&tar::archive::ArchiveInner<dyn std::io::Read>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:2819:12
|
2819 | pub struct Take<T> {
| ^^^^
note: required because it appears within the type `tar::entry::EntryIo<'_>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:47:10
|
47 | pub enum EntryIo<'a> {
| ^^^^^^^
note: required because it appears within the type `PhantomData<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/marker.rs:757:12
|
757 | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `alloc::raw_vec::RawVec<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:76:19
|
76 | pub(crate) struct RawVec<T, A: Allocator = Global> {
| ^^^^^^
note: required because it appears within the type `Vec<tar::entry::EntryIo<'_>>`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:397:12
|
397 | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
| ^^^
note: required because it appears within the type `tar::entry::EntryFields<'_>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:30:12
|
30 | pub struct EntryFields<'a> {
| ^^^^^^^^^^^
note: required because it appears within the type `tar::Entry<'_, Box<(dyn std::io::Read + 'static)>>`
--> /home/harsha/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tar-0.4.44/src/entry.rs:23:12
|
23 | pub struct Entry<'a, R: 'a + Read> {
| ^^^^^
note: required because it's used within this closure
--> src/main.rs:70:28
|
70 | let sjh = ts.spawn(move || {
| ^^^^^^^
note: required by a bound in `Scope::<'scope, 'env>::spawn`
--> /home/harsha/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:197:28
|
195 | pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
| ----- required by a bound in this associated function
196 | where
197 | F: FnOnce() -> T + Send + 'scope,
| ^^^^ required by this bound in `Scope::<'scope, 'env>::spawn`
For more information about this error, try `rustc --explain E0277`.
Share
Improve this question
edited Mar 19 at 16:06
cafce25
27.9k5 gold badges45 silver badges58 bronze badges
asked Mar 19 at 13:22
HarryHarry
3,2481 gold badge24 silver badges46 bronze badges
4
|
1 Answer
Reset to default 2This simply can't work with the tar
crate because it uses thread-unsafe cells internally. In particular, Archive
is !Sync
, so you cannot share a &Archive
with a thread, even a scoped one, and Entry
contains a reference to an Archive
. (The rule is that &T: Send
if and only if T: Sync
. Scoped threads resolve the issue of sending values with a shorter lifetime than 'static
to threads, but they do not obviate the requirement that the value be Send
.)
Instead, you can alter your approach. Rather than relying on threads to do this, you can simply change write_to_file
to accept a closure that can be called after every successful write, receiving the number of bytes written.
fn write_to_file<R: Read, W: Write>(mut reader: R, mut writer: W, mut monitor: impl FnMut(usize)) {
let mut buf = vec![0; 1024];
let mut bytes_read = buf.capacity();
while bytes_read > 0 {
bytes_read = reader.read(&mut buf).expect("failed to read from the file");
if bytes_read > 0 {
writer
.write_all(&buf[..bytes_read])
.expect("failed to write to the file");
}
monitor(bytes_read);
}
writer.flush().expect("failed to flush the writer");
}
Then, instead of using threads, just provide a closure to handle displaying progress:
fn main() {
let mut archive = get_archive(ArchiveType::Tar, Path::new("abc.tar"));
let file_name = "def.txt".to_string();
let src_file = get_entry(&mut archive, &file_name).expect("error while getting entry");
let metadata = metadata(src_file.path().unwrap()).unwrap();
let dst_file = &File::create("b.txt").expect("unable to create the file");
let mut progress = 0;
write_to_file(src_file, dst_file, |read| {
progress += u64::try_from(read).unwrap();
println!("{}", 100 * progress / metadata.len());
});
}
This approach is simple and effective, though you could argue that it's conceptually flawed as write_to_file
is "overloaded" in that it's doing more than one thing.
Perhaps a cleaner approach would be to create a Write
wrapper that handles the immediate inspection of the number of written bytes.
struct NotifyOnWrite<W, F> {
write: W,
notify: F,
}
impl<W, F> NotifyOnWrite<W, F> {
pub fn new(write: W, notify: F) -> Self {
Self { write, notify }
}
}
impl<W: Write, F: FnMut(usize)> Write for NotifyOnWrite<W, F> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.write.write(buf).inspect(|&bytes| (self.notify)(bytes))
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
self.write
.write_all(buf)
.inspect(|_| (self.notify)(buf.len()))
}
fn flush(&mut self) -> std::io::Result<()> {
self.write.flush()
}
}
Now, given your original definition of write_to_file
, we can do this:
fn main() {
let mut archive = get_archive(ArchiveType::Tar, Path::new("abc.tar"));
let file_name = "def.txt".to_string();
let src_file = get_entry(&mut archive, &file_name).expect("error while getting entry");
let metadata = metadata(src_file.path().unwrap()).unwrap();
let dst_file = &File::create("b.txt").expect("unable to create the file");
let mut progress = 0;
write_to_file(
src_file,
NotifyOnWrite::new(dst_file, |read| {
progress += u64::try_from(read).unwrap();
println!("{}", 100 * progress / metadata.len());
}),
);
}
This way we can "inject" functionality into the Write
implementation without write_to_file
needing to know or care about it.
This is an example of composition. NotifyOnWrite
is a composition layer that allows us to add functionality to an existing Write
.
&File
from theEntry
struct? – Harry Commented Mar 19 at 16:17&File::open(entry.path().as_ref().ok().unwrap()).expect("error while opening src file");
, but this doesn't work as the input is an invalid path – Harry Commented Mar 19 at 16:58Entry
at all. – cafce25 Commented Mar 19 at 17:22&File
from theEntry
" There is noFile
in anEntry
so no. – cafce25 Commented Mar 19 at 21:42