Я пытаюсь извлечь файл .tar.bz (или .tar. что угодно на самом деле), а также могу получить отчет о прогрессе 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
как изменяемый; Я использую только функцию поиска, чтобы получить текущую позицию.
Есть ли способ обойти это, либо заставив компилятор игнорировать изменяемое заимствование, либо получив позицию каким-либо неизменным способом?
Просмотрите, как создать минимальный воспроизводимый пример, а затем редактировать свой вопрос, чтобы включить его. Мы не можем сказать, какие ящики, типы, черты, поля и т. д. Присутствуют в коде. В частности, мы не можем сказать, откуда взялись BzDecoder
и Archive
, а также некоторые другие признаки. Попробуйте создать что-то, что воспроизводит вашу ошибку на Ржавчина Детская площадка, или вы можете воспроизвести это в новом проекте Cargo. Также есть Подсказки MCVE, специфичные для ржавчины.
Файлы немного особенные. Обычные методы read()
, seek()
и write()
(определенные по признакам Read
, Seek
и Write
) берут 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
, изменяемой ссылки на ссылку на файл.
Ваш код передает &f
в BzDecoder::new()
, создавая неизменный заимствование. Позже вы вызываете f.seek(SeekFrom::Current(0))
, который передает f
в seek
по изменяемой ссылке. Однако это недопустимо, поскольку у вас уже есть неизменяемое заимствование файла. Решение состоит в том, чтобы вместо этого использовать реализацию Seek
на &File
:
(&mut &f).seek(SeekFrom::Current(0))
или чуть проще
(&f).seek(SeekFrom::Current(0))
Это создает только второе неизменяемое заимствование, которое разрешено правилами Rust для ссылок.
Я создал пример игровой площадки, демонстрирующий, что это работает. Если вы замените (&f)
на f
, вы получите исходную ошибку.
Хорошее спасибо. Я не знал о реализации трейтов & File. А как работает версия (&f).seek()
? Разве указатель не должен изменяться в соответствии с чертой?
См. Также: Почему можно реализовать чтение для неизменяемой ссылки на файл?.
@ thanasis2028 (&f).seek()
работает по той же причине, что и f.seek()
. Сначала выполняется поиск методов по типу самого приемника, скажем, T
, затем по &T
, затем по &mut T
, затем по объектам, полученным путем итеративного разыменования приемника. Подробнее см. этот ответ.
seek()
принимает&mut self
, поэтому здесь происходит изменяемое заимствование. Глядя на документы ящика, не могли бы вы вместо этого использоватьdecoder.total_in()
?