我有一个为某些容器实现移动语义的最小代码示例:

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”操作执行一些其他操作,并不一定返回相同的容器类型。

最佳答案

rules of references state
在任何给定的时间,您可以拥有但不能同时拥有:
一个可变引用。
任意数量的不可变引用。
具有不可变引用的代码可以工作,因为这些引用可以自由复制。带有可变引用的代码失败,因为据编译器所知,您需要有两个并发可变引用:在new中保存的引用,然后mem::forget中可能也需要它。
作为人类,我们认识到mem::forget无法进入我们结构的内脏。这就是unsafe代码的作用:当编译器不能保证我们拥有的代码是真正安全的时候。
一个小的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();
}

关于rust - 如何在具有可变字段的结构上实现 move 语义?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48485203/

10-10 01:03
查看更多