最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

multithreading - How to passmove a tar entry to a thread - Stack Overflow

programmeradmin3浏览0评论

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
  • @cafce25 Is it possible to get the file handle reference &File from the Entry struct? – Harry Commented Mar 19 at 16:17
  • @cafce25 I though of doing like this &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:58
  • 1 Even if that worked it would give you a totally unrelated file handle happening to point to the same file, it's seek position would not be related to the one from the Entry at all. – cafce25 Commented Mar 19 at 17:22
  • "Is it possible to get the file handle reference &File from the Entry" There is no File in an Entry so no. – cafce25 Commented Mar 19 at 21:42
Add a comment  | 

1 Answer 1

Reset to default 2

This 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.

发布评论

评论列表(0)

  1. 暂无评论