无法从&mut self借用文件(错误消息:无法移动已借用的内容)

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

为什么会发生这种情况?我应该怎么做才能解决它?

1个回答

37
在print中,self的类型为&mut Foo,即它是对类型为Foo的值的借用可变引用。在Rust中,默认情况下,类型会移动所有权,也就是说,按值获取某些内容将静态无效化源并阻止程序员再次使用它(除非重新初始化)。在这种情况下,unwrap的签名为:
impl Option<T> {
    fn unwrap(self) -> T { ...

即,它按值获取 Option 值,因此尝试使用它的所有权。因此,self.maybe_file.unwrap() 尝试使用 maybe_file 中的数据,这将导致 self 指向部分无效的数据(在此之后使用 maybe_file 字段是非法的)。编译器无法通过借用引用来强制执行此操作,因为它们始终可以指向任何地方,因此移动出是非法的。

幸运的是,我们可以避免这个问题:as_ref方法将&Option<T>转换为Option<&T>as_mut方法将&mut Option<T>转换为Option<&mut T>。由此产生的Option不再在引用后面,因此可以使用unwrap来消耗它:

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

这有些不同,因为file的类型是&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一样。


1
非常感谢,这个答案帮了我很多! - Reignbeaux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接