关于 rust:如何将值从 Drop::drop() 的参数中移出?

How can I move a value out of the argument to Drop::drop()?

我正在使用 gfx-hal,这要求我创建需要使用特定于其类型的函数显式销毁的资源。我想将这些类型的实例存储在结构中,并且我还想将清理它们与拥有结构的生命周期联系起来,而不是手动管理它们的生命周期并可能在 GPU/驱动程序中拥有对象永远。

但是,destroy 系列函数中的所有函数都直接采用类型,而不是引用,所以当我尝试从我的结构中传递它们时,我会收到如下错误:

1
2
3
4
5
error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
 --> src/lib.rs:9:18
  |
9 |         destroyT(self.member)
  |                  ^^^^^^^^^^^ cannot move out of here

似乎应该有办法解决这个问题,因为我目前在 Drop::drop 函数本身中,所以 self 已经"消耗"了。如何将这些类型的实例从 self 中获取为 T,而不是 &T

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct T;

struct S {
    member: T,
}

impl Drop for S {
    fn drop(&mut self) {
        destroyT(self.member)
    }
}

// elsewhere, in a library

fn destroyT(t: T) {
    //...
}


最安全、最简单的方法是使用 Option:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct T;

impl Drop for T {
    fn drop(&mut self) {
        println!("dropping T");
    }
}

struct S {
    member: Option< T >,
}

impl Drop for S {
    fn drop(&mut self) {
        if let Some(t) = self.member.take() {
            destroy_t(t);
        }
    }
}

fn destroy_t(_t: T) {
    println!("destroy T");
}

fn main() {
    let _x = S { member: Some(T) };
}

您可以选择使用带有 ManuallyDrop 的不安全代码并将当前值换成未初始化的代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use std::mem::{self, ManuallyDrop};

struct T;

impl Drop for T {
    fn drop(&mut self) {
        println!("dropping T");
    }
}

struct S {
    member: ManuallyDrop< T >,
}

impl Drop for S {
    fn drop(&mut self) {
        unsafe {
            let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
            destroy_t(valid_t);
            // do *not* call ManuallyDrop::drop
        };
    }
}

fn destroy_t(_t: T) {
    println!("destroy T");
}

fn main() {
    let _x = S {
        member: ManuallyDrop::new(T),
    };
}

1 使用 mem::uninitialized 非常危险,而且很难正确使用,尤其是在通用上下文中。使用每晚的 MaybeUninit,这可能看起来像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#![feature(maybe_uninit)]

use std::mem::{self, ManuallyDrop, MaybeUninit};

struct T;

impl Drop for T {
    fn drop(&mut self) {
        println!("dropping T");
    }
}

struct S {
    member: ManuallyDrop<MaybeUninit< T >>,
}

impl Drop for S {
    fn drop(&mut self) {
        let invalid_t = MaybeUninit::uninitialized();
        let valid_t = mem::replace(&mut *self.member, invalid_t);
        let valid_t = unsafe { valid_t.into_inner() };
        destroy_t(valid_t);
        // do *not* call ManuallyDrop::drop
    }
}

fn destroy_t(_t: T) {
    println!("destroy T");
}

fn main() {
    let _x = S {
        member: ManuallyDrop::new(MaybeUninit::new(T)),
    };
}

参见:

  • 如何将一个字段移出实现 Drop 特征的结构?