无法从&mut self借用文件(错误信息:无法移出借来的内容)

for*_*ave 18 rust borrow-checker

use std::fs::File;
use std::io::Read;

pub struct Foo {
    maybe_file: Option<File>,
}

impl Foo {
    pub fn init(&mut self) {
        self.maybe_file = Some(File::open("/proc/uptime").unwrap());
    }

    pub fn print(&mut self) {
        let mut file = self.maybe_file.unwrap();
        let mut s = String::new();
        file.read_to_string(&mut s).unwrap();
        println!("Uptime: {}", s);
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

编译这将给我:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:14:24
   |
14 |         let mut file = self.maybe_file.unwrap();
   |                        ^^^^ cannot move out of borrowed content
Run Code Online (Sandbox Code Playgroud)

为什么会这样?我该怎么做才能解决它?

huo*_*uon 35

self已键入&mut Fooprint,即,它是一个借用的可变引用类型的值Foo.Rust中的类型默认情况下移动所有权,也就是说,取值by-value将静态地使源无效并阻止程序员再次使用它(除非它被重新初始化).在这种情况下,unwrap有签名:

impl Option<T> {
    fn unwrap(self) -> T { ...
Run Code Online (Sandbox Code Playgroud)

也就是说,它取Option价值按值,从而试图消耗它的所有权.因此,self.maybe_file.unwrap()试图消耗maybe_file其中会留下self部分无效数据的数据(maybe_file之后使用该字段是非法的).编译器无法使用借用的引用强制执行此操作,这些引用必须始终有效,因为它们可以指向任何位置,因此移出是非法的.

幸运的是,人们可以避免这个问题:该as_ref方法创建了一个Option<&T>out,&Option<T>并且该as_mut方法创建了一个Option<&mut T>out &mut Option<T>.结果Option不再是引用的后面,因此通过unwrap以下方式使用它是合法的:

let mut file = self.maybe_file.as_mut().unwrap();
Run Code Online (Sandbox Code Playgroud)

这略有不同,因为file有类型&mut File而不是File,但幸运的&mut File是其余代码所需的全部内容.

另一种方法是使用手动模式匹配:

match self.maybe_file {
    Some(ref mut file)  => println!(...),
    None => panic!("error: file was missing")
}
Run Code Online (Sandbox Code Playgroud)

这与.as_mut().unwrap()更明确的做法完全相同:ref mut创建一个直接指向占用内存的引用self.maybe_file,就像as_mut.