假设我有一个带有析构函数的铁 rust struct:

pub struct D1 {
    a: String,
    b: String
}

impl Drop for D1 {
    fn drop(&mut self) {
        println!("{0}", self.a)
    }
}

我想实现一个方法,将部分或所有字段移出对象,并在此过程中销毁它.显然,如果对象的析构函数运行,这是不能安全完成的-它将try 不安全地从移动的字段中读取.然而,如果析构函数以某种方式被 suppress ,它应该是安全的.但我找不到一种方法来压制 destruct 者,让移动变得安全.

mem::forget不起作用,因为当涉及到析构函数时,编译器不会特别对待它:

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let (a, b) = (self.a, self.b); // cannot move here
        mem::forget(self);
        (a, b)
    }
}

ManuallyDrop也不起作用-它只通过引用提供内部数据,防止移动:

use std::mem;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let md = mem::ManuallyDrop::new(self);
        (md.a, md.b) // cannot move out of dereference
    }
}
use std::mem;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let md = mem::ManuallyDrop::new(self);
        (md.value.a, md.value.b) // field `value` is private
    }
}

我大概可以通过使用unsafe个代码来解决这个问题,但我想知道是否有一种安全的方法来做到这一点--我是不是错过了一些API或其他技术来告诉Rust我可以安全地移出一个值,因为我不打算运行析构函数?

推荐答案

在安全代码中,不可能使实现Drop的类型处于未初始化状态.但是,您可以使用伪值对其进行初始化:

use std::mem;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        (mem::take(&mut self.a), mem::take(&mut self.b))
    }
}

这将使self保留空字符串(甚至不分配任何内存).当然,Drop实现必须容忍这一点,并且不能做一些不受欢迎的事情.

如果您实际情况下的字段类型没有方便的占位符值,那么您可以将这些字段设置为Option.当Drop::drop()本身需要移出self时,这也是一种常见的技术.

Rust相关问答推荐

trait 中self 的显式生命周期似乎导致E0499无法在循环中多次borrow * emits 器作为可变的

即使参数和结果具有相同类型,fn的TypId也会不同

如何导出 rust 色二进制文件中的符号

定义采用更高级类型泛型的性状

在UdpSocket上使用sendto时的隐式套接字绑定

如何用Axum/Tower压缩Html内容?

如何高效地将 struct 向量中的字段收集到单独的数组中

为什么`str`类型可以是任意大小(未知大小),而`string`类型的大小应该是已知的?

为什么RefCell没有与常规引用相同的作用域?

如何轮询 Pin>?

通过异常从同步代码中产生yield 是如何工作的?

这是什么:`impl Trait for T {}`?

为什么 GAT、生命周期和异步的这种组合需要 `T: 'static`?

当我编译 Rust 代码时,我是否缺少 AVX512 的目标功能?

闭包返回类型的生命周期规范

是否可以通过可变引用推进可变切片?

由特征键控的不同 struct 的集合

通用类型,不同于输入类型,作为函数的返回值

Rust:为什么在 struct 中borrow 引用会borrow 整个 struct?

为什么可以从不可变 struct 的字段中移动?