20
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() {}

编译它会给我:

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

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

4

1 回答 1

36

selfhas type &mut Fooin print,也就是说,它是对 type 值的借用可变引用Foo。默认情况下,Rust 中的类型移动所有权,也就是说,按值获取某些东西会使源静态无效并阻止程序员再次使用它(除非它被重新初始化)。在这种情况下,unwrap具有签名:

impl Option<T> {
    fn unwrap(self) -> T { ...

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

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

let mut file = self.maybe_file.as_mut().unwrap();

这略有不同,因为file有 type&mut File而不是File,但幸运&mut File的是其余代码所必需的。

完成这项工作的另一种方法是使用手动模式匹配:

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

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

于 2015-01-19T23:10:45.757 回答