"не может выйти из переменной, потому что она заимствована" при вращении переменных

Я пишу программу, которая пишет в файл и вращает файл, в который он пишет время от времени. Когда я проверяю, чтобы вращать файл, я не могу изменить файл, так как он заимствован моей структурой. Даже если я 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 уже был покорно заимствован.

Другие вопросы по тегам