【问题标题】:How to move a field whose type does not implement Default from a struct which implements Drop?如何从实现 Drop 的结构中移动类型未实现 Default 的字段?
【发布时间】:2020-07-18 15:48:10
【问题描述】:

有一些similarquestions,但是theanswers要求字段实现Default或者用字段的类型初始化另一个值。


我们有一个Node,其中有一个value 类型为T

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

它有一个将valueNode移动的方法:

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

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

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

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

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

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

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


Rust Playground

【问题讨论】:

  • 那不是因为答案不适合你,所以你的问题不会重复。
  • @Stargateur 我在问题顶部提到的那个问题,我已经解释了为什么它在这里不适用。
  • 再一次,这个问题完全是重复的,需要默认值的答案是无关紧要的。无论您将使用多少不安全,都无法欺骗编译器执行您想要的操作。要么使用 Option,使用默认值,Clone 值,不实现 Drop。
  • 这不是无关紧要的。我也认为可能没有办法安全地做到这一点,但我可能是错的(即实际上有),因此问题。如果方法不安全也没关系。

标签: rust


【解决方案1】:

我不知道不使用 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 将所需的值移出。然后我们需要在Node 上使用mem::forget 以确保它的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&lt;T&gt;, 它实现了 Drop特质

我原以为match 表达式拥有所有self 的所有权并将其分解为它的组件,drop 将无法在self 上调用,因此没有理由编译器抱怨。但显然它不是这样工作的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-08
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多