“cannot move out of variable because it is borrowed” when rotating variables

試著忘記壹切 提交于 2019-11-28 07:45:18

问题


I am writing a program that writes to a file and rotates the file it's writing to every now and then. When I check to rotate the file, I can't seem to change the file since it is borrowed by my struct. Even if I drop the instance of the struct, I can't seem to regain ownership of the file to rename it. Here is my example:

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
    }
}

I know I can get this to work by moving the file creation into the new function call of FileStruct instead of having an intermediate variable, file, but I would like to know why this method where I forcibly drop all the variables where all the variables references should be returned doesn't work.


回答1:


As the std::mem::drop documentation says,

While this does call the argument's implementation of Drop, it will not release any borrows, as borrows are based on lexical scope.

So even if you call drop, file will remain borrowed nonetheless.




回答2:


Dropping tmp does not "release the borrow" of file because borrowing is lexically scoped. It's "active" as long as the program execution is within the lexical scope that contains tmp even if you drop it. What you intended to do might be possible in the future if/once "non-lexical scopes" are supported. Until then, you can make it work with 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
}

The trick here is that given a shared reference to a RefCell<T> you can (eventually) get a &mut T via borrow_mut(). The compile-time borrow checker is pleased because we only use a shared reference on the surface and it's OK to share file like that. Mutable aliasing is avoided by checking at runtime whether the internal T has already been mutably borrowed.



来源:https://stackoverflow.com/questions/34007692/cannot-move-out-of-variable-because-it-is-borrowed-when-rotating-variables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!