首页 > 解决方案 > 为什么 RefCell 的 `into_inner` 需要移动?

问题描述

我有一种情况,我必须通过&mut self. 看一看:

pub struct MyStruct {
    //no copy trait
    device: Device
}

impl MyStruct {
    pub fn finalize(&mut self) {
        //error: cannot move since I borrowed 
        let interface = InterfaceBuilder::new(self.device)
    }
}

首先,为什么我不能从借来的可变引用中移出一些东西?借用的可变数据是排他的,没有其他代码正在调查它。

好吧,为了解决这个问题,我改为

pub struct MyStruct {
    //no copy trait
    device: RefCell<Device>
}

impl MyStruct {
    pub fn finalize(&mut self) {
        //error on `self.device`: cannot move out of `self.device` which is behind a mutable reference
        let interface = InterfaceBuilder::new(self.device.into_inner())
    }
} 

我知道为什么会发生错误:

pub fn into_inner(self) -> T

呼叫into_inner使self.device移动。为什么RefCell根本没有实现pub fn into_inner(&mut self) -> T?我看不出有什么问题。

标签: rust

解决方案


您不能移出可变引用,因为这会使原始对象不完整。

考虑这段代码:

struct MyStruct {
    s: String
}

fn finalize(f: &mut MyStruct) {
    let _x = f.s; //error E0507!
}

fn main() {
    let mut my = MyStruct {
        s: "hi".into()
    };
    finalize(&mut my);
    println!("{}", my.s); //what should this do?
}

然后,RefCell::into_inner(&mut self) -> T有同样的问题。你可以连续调用它两次,你会得到两个T值,而之前只有一个。而且,对于非Copy类型是不可能的。

如果您希望此函数消耗内部值,则它可能也应该消耗外部值:

fn finalize(f: MyStruct) {
    let _x = f.s;
}

如果你真的想从可变引用中移出一个值,你必须在它的位置留下一些有效的东西。最简单的方法是声明 anOption并使用take()来窃取并用 a 替换它None

struct MyStruct {
    s: Option<String>
}

fn finalize(f: &mut MyStruct) {
    let _x = f.s.take();
}

自然地,Option::take返回一个Option,这样如果你调用它两次,第二次你就会得到None。如果你是肯定的,你就有一个可以做的价值take().uwnrap()

或者,如果您的字段类型是Default,您可以使用std::mem::take它将其替换为默认创建的值:

struct MyStruct {
    s: Vec<i32>
}

fn finalize(f: &mut MyStruct) {
    let _x = std::mem::take(&mut f.s);
}

PS #1:有Cell::take(&self) -> T,但只有在T实现时Default。它的工作原理std::mem::take与非可变引用相同。

PS #2:还有unsafe fn ManuallyDrop::take(slot: &mut ManuallyDrop<T>) -> T, 旨在用于高级drop实现。但它unsafe永远不应该是你的第一选择:如果你调用它两次,你会得到undefined behavior


推荐阅读