rust - 为什么 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
?我看不出有什么问题。
解决方案
您不能移出可变引用,因为这会使原始对象不完整。
考虑这段代码:
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。
推荐阅读
- python - 无法建立新连接:[Errno 111] Connection refused'))
- node.js - Ramda 中 Lodash _.transform 的等价物是什么?
- c++ - 将具有相反操作数的两个函数重构为一个
- javascript - 我可以使用 javascript 或 jquery 以及从 MVC 中的 razor 生成的动态单选按钮吗
- angular - 如何在 Angular 8 中降低 Material datepicker 日历的宽度和高度?
- c# - Boostrap 4 模式中的输入元素不是 Asp.Net 形式的回发
- ffmpeg - 为什么 ffmpeg 需要 DNS 解析器作为其依赖项?
- sql - Oracle SQL 会话的生命周期是什么?
- html - 我如何将其居中
- excel - Passing a variable produces error code "ByRef argument type mismatch" for "..."