如何在具有可变字段的结构上实现移动语义?
How do I implement move semantics on a struct with a mutable field?
我有一个为某些容器实现移动语义的最小代码示例:
use std::mem;
impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
fn drop(&mut self) {}
}
struct Cointainer<'a, T: 'a + ?Sized> {
item: &'a /* mut */ T,
}
impl<'a, T> Cointainer<'a, T> {
fn mv(self) -> Cointainer<'a, T> {
let new = Cointainer { item: /* &mut */ self.item };
mem::forget(self);
new
}
}
fn main() {}
编译和工作没有任何问题。
我意识到我需要改变 Cointainer::item
引用的值,所以我做了引用 mut
。当我这样做时,我得到:
error[E0505]: cannot move out of `self` because it is borrowed
--> src/main.rs:14:21
|
13 | let new = Cointainer { item: /* &mut */ self.item };
| --------- borrow of `*self.item` occurs here
14 | mem::forget(self);
| ^^^^ move out of `self` occurs here
我需要创建一个新容器并在那里转移 item
的所有权,并删除旧容器。
这个例子是人为的。实际的 "move" 操作会做一些其他的事情,不一定 return 相同的容器类型。
At any given time, you can have either but not both of:
- One mutable reference.
- Any number of immutable references.
具有不可变引用的代码之所以有效,是因为它们可以自由复制。带有可变引用的代码会失败,因为就编译器而言,您需要有两个并发的可变引用:new
中保存的引用,然后 mem::forget
可能也需要它。
作为人类,我们认识到 mem::forget
无法访问我们结构的内部结构。这就是 unsafe
code 的用途:当编译器无法保证我们拥有的代码是真正安全的时候。
一个小的 unsafe
块和一些转换为原始指针并返回解决了这个问题。与任何不安全代码一样,它应该有一个大注释块来解释为什么编译器不理解它以及为什么它真正安全。
impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
fn drop(&mut self) {
println!("dropping 1: {:p}", self.item)
}
}
struct Cointainer<'a, T: 'a + ?Sized> {
item: &'a mut T,
}
impl<'a, T> Cointainer<'a, T> {
fn into_inner(self) -> &'a mut T {
// I copied this code from Stack Overflow but didn't read
// the warning about explaining why it's safe. Sure hope
// this doesn't cause any bugs!
unsafe {
let x = self.item as *mut _;
std::mem::forget(self);
&mut *x
}
}
fn mv(self) -> Cointainer2<'a, T> {
let item = self.into_inner();
Cointainer2 { item }
}
}
struct Cointainer2<'a, T: 'a + ?Sized> {
item: &'a mut T,
}
impl<'a, T: 'a + ?Sized> Drop for Cointainer2<'a, T> {
fn drop(&mut self) {
println!("dropping 2: {:p}", self.item)
}
}
fn main() {
let mut a = String::new();
let c1 = Cointainer { item: &mut a };
let c2 = c1.mv();
}
我有一个为某些容器实现移动语义的最小代码示例:
use std::mem;
impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
fn drop(&mut self) {}
}
struct Cointainer<'a, T: 'a + ?Sized> {
item: &'a /* mut */ T,
}
impl<'a, T> Cointainer<'a, T> {
fn mv(self) -> Cointainer<'a, T> {
let new = Cointainer { item: /* &mut */ self.item };
mem::forget(self);
new
}
}
fn main() {}
编译和工作没有任何问题。
我意识到我需要改变 Cointainer::item
引用的值,所以我做了引用 mut
。当我这样做时,我得到:
error[E0505]: cannot move out of `self` because it is borrowed
--> src/main.rs:14:21
|
13 | let new = Cointainer { item: /* &mut */ self.item };
| --------- borrow of `*self.item` occurs here
14 | mem::forget(self);
| ^^^^ move out of `self` occurs here
我需要创建一个新容器并在那里转移 item
的所有权,并删除旧容器。
这个例子是人为的。实际的 "move" 操作会做一些其他的事情,不一定 return 相同的容器类型。
At any given time, you can have either but not both of:
- One mutable reference.
- Any number of immutable references.
具有不可变引用的代码之所以有效,是因为它们可以自由复制。带有可变引用的代码会失败,因为就编译器而言,您需要有两个并发的可变引用:new
中保存的引用,然后 mem::forget
可能也需要它。
作为人类,我们认识到 mem::forget
无法访问我们结构的内部结构。这就是 unsafe
code 的用途:当编译器无法保证我们拥有的代码是真正安全的时候。
一个小的 unsafe
块和一些转换为原始指针并返回解决了这个问题。与任何不安全代码一样,它应该有一个大注释块来解释为什么编译器不理解它以及为什么它真正安全。
impl<'a, T: 'a + ?Sized> Drop for Cointainer<'a, T> {
fn drop(&mut self) {
println!("dropping 1: {:p}", self.item)
}
}
struct Cointainer<'a, T: 'a + ?Sized> {
item: &'a mut T,
}
impl<'a, T> Cointainer<'a, T> {
fn into_inner(self) -> &'a mut T {
// I copied this code from Stack Overflow but didn't read
// the warning about explaining why it's safe. Sure hope
// this doesn't cause any bugs!
unsafe {
let x = self.item as *mut _;
std::mem::forget(self);
&mut *x
}
}
fn mv(self) -> Cointainer2<'a, T> {
let item = self.into_inner();
Cointainer2 { item }
}
}
struct Cointainer2<'a, T: 'a + ?Sized> {
item: &'a mut T,
}
impl<'a, T: 'a + ?Sized> Drop for Cointainer2<'a, T> {
fn drop(&mut self) {
println!("dropping 2: {:p}", self.item)
}
}
fn main() {
let mut a = String::new();
let c1 = Cointainer { item: &mut a };
let c2 = c1.mv();
}