"не может выйти из переменной, потому что она заимствована" при вращении переменных
Я пишу программу, которая пишет в файл и вращает файл, в который он пишет время от времени. Когда я проверяю, чтобы вращать файл, я не могу изменить файл, так как он заимствован моей структурой. Даже если я drop
экземпляр структуры, я не могу восстановить право собственности на файл, чтобы переименовать его. Вот мой пример:
use std::fs::File;
use std::io::{Write};
use std::mem::{drop};
pub struct FileStruct<W: Write> {
pub writer: Option<W>,
}
impl <W: Write> FileStruct<W> {
pub fn new(writer: W) -> FileStruct<W> {
FileStruct {
writer: Some(writer),
}
}
}
fn main() {
let mut file = File::create("tmp.txt").unwrap();
let mut tmp = FileStruct::new(&mut file);
loop {
if true { //will be time based if check
drop(tmp);
drop(file);
file = File::create("tmp2.txt").unwrap();
tmp = FileStruct::new(&mut file);
}
// write to file
}
}
Я знаю, что могу заставить это работать, переместив создание файла в new
вызов функции FileStruct
вместо того, чтобы иметь промежуточную переменную, file
, но я хотел бы знать, почему этот метод, где я принудительно отбрасываю все переменные, где должны быть возвращены все ссылки на переменные, не работает.
2 ответа
Как std::mem::drop
документация говорит,
В то время как это вызывает реализацию аргумента
Drop
, он не выдаст заимствования, так как заимствования основаны на лексической сфере.
Так что даже если вы позвоните drop
, file
тем не менее останется заимствованным.
Опустившись tmp
не "выпустить заем" из file
потому что заимствование является лексически ограниченным. Он "активен", пока выполнение программы находится в лексической области, содержащей tmp
даже если ты уронишь это. То, что вы намеревались сделать, может стать возможным в будущем, если / раз поддерживаются "нелексические области". До тех пор, вы можете заставить его работать с RefCell
:
use std::cell::RefCell;
use std::io::{ self, Write };
/// wraps a reference to a RefCell<W>
struct RefCellWriteRef<'a, W: 'a>(&'a RefCell<W>);
/// implement Write for when W is Write
impl<'a, W: Write + 'a> Write for RefCellWriteRef<'a, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut w = self.0.borrow_mut();
w.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
let mut w = self.0.borrow_mut();
w.flush()
}
}
fn main() {
let file: RefCell<Vec<u8>> = RefCell::new(Vec::new());
// use RefCellWriteRef(&file) instead of &mut file
let mut tmp = RefCellWriteRef(&file);
for iter in 0..10 {
if iter == 5 {
drop(tmp);
file.borrow_mut().clear(); // like opening a new file
tmp = RefCellWriteRef(&file);
}
tmp.write(b"foo").unwrap();
}
drop(tmp);
println!("{}", file.borrow().len()); // should print 15
}
Хитрость в том, что при наличии общей ссылки на RefCell<T>
Вы можете (в конце концов) получить &mut T
с помощью borrow_mut()
, Средство проверки заимствования во время компиляции приятно, потому что мы используем только общую ссылку на поверхности, и все в порядке, чтобы поделиться file
как это. Изменяемый псевдоним можно избежать, проверяя во время выполнения, является ли внутренний T
уже был покорно заимствован.