如何将参数中的值移到Drop :: drop()?

Ben*_*ous 7 rust

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

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

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
Run Code Online (Sandbox Code Playgroud)

似乎应该有一些解决这个问题的方法,因为我目前处于Drop::drop函数本身,因此self已经"消耗"了.我如何获得这些类型的实例出来selfT,而不是&T

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) {
    //...
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 6

最安全的要做到这一点,最简单的方法是使用Option:

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) };
}
Run Code Online (Sandbox Code Playgroud)

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

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),
    };
}
Run Code Online (Sandbox Code Playgroud)

1使用mem::uninitialized非常危险的,很难得到正确,特别是在一般的环境中.使用夜间MaybeUninit,这可能看起来像

#![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)),
    };
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:


kmd*_*eko 5

您可以使用ManuallyDrop。它没有任何开销并且有清晰的 API。它使用起来不那么痛苦,MaybeUninit因为它不需要unsafe在其他地方使用(可以通过 simple 访问内部值Deref)并且只需要unsafe实际将值移出。

use std::mem::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) {
        // SAFETY: this is safe because we do not access `self.member` any more
        let member = unsafe { ManuallyDrop::take(&mut self.member) };
        destroy_t(member);
        
        // dropping a `ManuallyDrop` does nothing
    }
}

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

fn main() {
    let _x = S {
        member: ManuallyDrop::new(T),
    };
}
Run Code Online (Sandbox Code Playgroud)
use std::mem::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) {
        // SAFETY: this is safe because we do not access `self.member` any more
        let member = unsafe { ManuallyDrop::take(&mut self.member) };
        destroy_t(member);
        
        // dropping a `ManuallyDrop` does nothing
    }
}

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

fn main() {
    let _x = S {
        member: ManuallyDrop::new(T),
    };
}
Run Code Online (Sandbox Code Playgroud)

请记住,正如名称所示,ManuallyDrop将阻止内部值被删除,除非调用ManuallyDrop::takeManuallyDrop::into_inner(从而将其从保护中删除,并且将应用正常的删除规则)或ManuallyDrop::drop使用 if 。