首页 > 解决方案 > 如何从实现 Drop 的结构中移动类型未实现 Default 的字段?

问题描述

有一些类似的 问题,但答案需要 字段实现或以某种方式使用该字段的类型初始化另一个值。Default


我们有一个Node类型valueT

struct Node<T> {
    value: T,
    next: Option<Box<T>>
}

它有一个value从 a移动的方法Node

impl<T> Node<T> {
    fn value(self) -> T {
        self.value
    }
}

上面的代码编译。但是如果我们Drop实现Node

impl<T> Drop for Node<T> {
    fn drop(&mut self) {}
}

然后我们会得到一个编译错误:

error[E0509]: cannot move out of type `Node<T>`, which implements the `Drop` trait
   |         self.value
   |         ^^^^^^^^^^
   |         |
   |         cannot move out of here
   |         move occurs because `self.value` has type `T`, which does not implement the `Copy` trait

我猜它不会编译,因为如果我们实现一个 custom Drop,我们需要确保如果 drop 发生在方法块value的末尾,则不要删除字段。value但是,我们无法检查;即使我们可以,编译器也不能静态地检查我们是否这样做了。

解决此问题的一种方法是将value字段存储为Option<T>. Option但是假设由于某些原因(开销等)我们不想使用,

我们还能做些什么来同时拥有自定义Dropvalue移动value字段的方法?

我想我们必须使用一些unsafe方法,这很好。


锈游乐场

标签: rust

解决方案


我不知道不使用的方法unsafe(尽管其他人可能),但这是一种使用方法unsafe

use std::{ptr, mem};

impl<T> Node<T> {
    fn value(mut self) -> T {
        unsafe {
            let v: T = ptr::read(&self.value);
            ptr::drop_in_place(&mut self.next);
            mem::forget(self);
            v
        }
    }
}

我们使用ptr::read将所需的值移出。然后我们需要使用mem::forgetonNode来确保它的drop方法没有被调用(否则value可能会被丢弃两次并导致未定义的行为)。为了防止next成员泄漏,我们使用ptr::drop_in_place运行它的drop方法。

有趣的是,这个安全代码不起作用:

impl<T> Node<T> {
    fn value(self) -> T {
        match self {
            Node {value, next: _} => value
        }
    }
}

它给出了同样的错误:

error[E0509]: cannot move out of type Node<T>,实现了 Droptrait

我本来希望match表达式拥有所有的所有权self并将其分解为它的组件,就没有办法drop被调用self,因此编译器也没有理由抱怨。但显然它不是这样工作的。


推荐阅读