如何在具有可变字段的结构上实现移动语义?

时间:2018-01-28 10:12:30

标签: rust move-semantics mutable

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

public func sendMessage<T>(message: T) where T: AbstractMessage {
  let json = try! String(data: JSONEncoder().encode(message), encoding: .utf8)!
  ...
}

编译和工作没有任何问题。

我意识到我需要改变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 的所有权,然后删除旧容器。

这个例子是人为的。实际的&#34;移动&#34;操作做了一些其他的事情,并没有必要返回相同的容器类型。

1 个答案:

答案 0 :(得分:1)

rules of references state

  
      
  1. 在任何时候,您可以拥有以下两种颜色,但不能同时拥有以下两种颜色:

         
        
    • 一个可变的参考文献。
    •   
    • 任意数量的不可变引用。
    •   
  2.   

使用不可变引用的代码有效,因为可以自由复制它们。具有可变引用的代码失败,因为就编译器而言,您需要具有两个并发可变引用: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();
}