如何在不获取 E0507 的情况下将包含 Vec to/from 的结构移动到静态数组?

How can I move structs containing Vec to/from a static array without getting E0507?

我需要一个静态结构数组,该结构包含一个 Vec。我可以管理实际值的生命周期。我收到以下错误:

: Mar23 ; cargo test
Compiling smalltalk v0.1.0 (/Users/dmason/git/AST-Smalltalk/rust)
error[E0507]: cannot move out of `dispatchTable[_]` as `dispatchTable` is a static item
  --> src/minimal.rs:32:44
   |
30 |         let old = ManuallyDrop::into_inner(dispatchTable[pos]);
   |                                            ^^^^^^^^^^^^^^^^^^ move occurs because `dispatchTable[_]` has type `ManuallyDrop<Option<Box<Dispatch>>>`, which does not implement the `Copy` trait

error: aborting due to previous error

这是一个最小的可编译示例:

#[derive(Copy, Clone)]
struct MethodMatch {
    hash: i64,
    method: Option<bool>,
}
#[derive(Clone)]
pub struct Dispatch {
    class: i64,
    table: Vec<MethodMatch>,
}

const max_classes : usize = 100;
use std::mem::ManuallyDrop;
const no_dispatch : ManuallyDrop<Option<Box<Dispatch>>> = ManuallyDrop::new(None);
static mut dispatchTable : [ManuallyDrop<Option<Box<Dispatch>>>;max_classes] = [no_dispatch;max_classes];
use std::sync::RwLock;
lazy_static! {
    static ref dispatchFree : RwLock<usize> = {RwLock::new(0)};
}
pub fn addClass(c : i64, n : usize) {
    let mut index = dispatchFree.write().unwrap();
    let pos = *index;
    *index += 1;
    replaceDispatch(pos,c,n);
}
pub fn replaceDispatch(pos : usize, c : i64, n : usize) -> Option<Box<Dispatch>> {
    let mut table = Vec::with_capacity(n);
    table.resize(n,MethodMatch{hash:0,method:None});
    unsafe {
        let old = ManuallyDrop::into_inner(dispatchTable[pos]);
        dispatchTable[pos]=ManuallyDrop::new(Some(Box::new(Dispatch{class:c,table:table})));
        old
    }
}

我的想法是 replaceDispatch 创建一个新的 Dispatch 选项对象,并用新值替换数组中的当前值,返回原始值,调用者将获得调度选项值并能够使用然后 drop/deallocate 对象。

我发现如果我在识别出的错误点之后添加 .clone() 它会编译。 但是 原始值永远不会被删除,所以(into_inner 是多余的并且)我正在制造内存泄漏!。我是否必须手动删除它(如果我能弄清楚如何)?我认为这就是 ManuallyDrop 给我带来的好处。理论上,如果我将 Vec 中的字段副本创建到副本中,它将指向旧数据,因此当该对象被删除时,内存将被释放。但是(a)这看起来很脏,(b)它有点丑陋,不必要的代码(我必须处理 Some/None 情况,查看 Vec 内部等),以及(c)我不能看我怎么做!!!!

正如编译器告诉你的那样,你不能将一个值移出其他人可以观察到的地方。但是因为你已经准备好替换,你可以使用 std::mem::replace:

pub fn replaceDispatch(pos: usize, c: i64, n: usize) -> Option<Box<Dispatch>> {
    ... table handling omitted ...
    unsafe {
        let old = std::mem::replace(
            &mut dispatchTable[pos],
            ManuallyDrop::new(Some(Box::new(Dispatch {
                class: c,
                table: table,
            }))),
        );
        ManuallyDrop::into_inner(old)
    }
}

Playground

事实上,由于您正在使用 Option 来管理 Dispatch 的生命周期,所以您根本不需要 ManuallyDrop,您也不需要Box: playground.