使用进度条提取档案-可变借阅错误

时间:2018-10-26 07:25:48

标签: file rust borrowing

我正在尝试提取.tar.bz文件(或任何实际的.tar.what文件),并且还能够获得xx%进度报告。到目前为止,我有这个:

pub fn extract_file_with_progress<P: AsRef<Path>>(&self, path: P) -> Result<()> {
    let path = path.as_ref();
    let size = fs::metadata(path)?;
    let mut f = File::open(path)?;
    let decoder = BzDecoder::new(&f);
    let mut archive = Archive::new(decoder);

    for entry in archive.entries()? {
        entry?.unpack_in(".")?;
        let pos = f.seek(SeekFrom::Current(0))?;
    }

    Ok(())
}

这个想法是使用pos/size来获取百分比,但是编译上面的函数会得到错误cannot borrow f as mutable because it is also borrowed as immutable。 我知道错误的含义,但我并没有真正使用f作为可变变量。我只使用seek函数来获取当前位置。

是否有一种解决方法,可以通过强制编译器忽略可变借用或以某种不可变的方式获取位置?

1 个答案:

答案 0 :(得分:8)

文件有点特殊。常用的read()seek()write()方法(在ReadSeekWrite特征上定义)采用self可变参考:

fn read(&mut self, buf: &mut [u8]) -> Result<usize>
fn seek(&mut self, pos: SeekFrom) -> Result<u64>
fn write(&mut self, buf: &[u8]) -> Result<usize>

但是,所有提到的特征也都针对&File实现,即针对文件的不可变引用:

因此,即使您只有对该文件的只读引用,也可以修改该文件。对于这些实现,Self类型为&File,因此通过可变引用接受self实际上意味着接受&mut &File,即对文件引用的可变引用。 / p>

您的代码将&f传递给BzDecoder::new(),从而创建了一个不变的借位。稍后您调用f.seek(SeekFrom::Current(0)),它通过可变引用将f传递给seek。但是,这是不允许的,因为您已经拥有文件的不可变借项。解决方案是改为在Seek上使用&File实现:

(&mut &f).seek(SeekFrom::Current(0))

或更简单

(&f).seek(SeekFrom::Current(0))

这只会创建第二个不可变的借用,Rust的规则允许引用。

我创建了一个playground example demonstrating that this works。如果将(&f)替换为f,则会得到最初的错误。