You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不执行析构函数的情况下移出Rust结构体字段?

如何安全移出带析构函数的Rust结构体字段?

先看定义的结构体,它实现了Drop trait,析构函数会访问字段a:

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

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

我们需要实现一个方法,在不执行结构体析构函数的前提下移出它的全部字段——如果析构函数被执行,会访问已被移出的字段导致未定义行为,但抑制析构函数后操作是安全的。

为什么常规方法无法实现?

  • mem::forget 不可行:编译器不允许从实现了Drop的结构体中直接移出字段,即使后续调用mem::forget也不行,编译期会提前阻止这种可能违反安全规则的操作:
impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let (a, b) = (self.a, self.b); // 编译错误:无法从实现Drop的结构体移出字段
        mem::forget(self);
        (a, b)
    }
}
  • ManuallyDrop 直接访问无效:ManuallyDrop的内部值是私有字段,无法直接访问;通过引用访问也无法移出数据,因为引用不允许转移所有权:
use std::mem;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let md = mem::ManuallyDrop::new(self);
        (md.a, md.b) // 编译错误:无法从解引用的ManuallyDrop中移出数据
    }
}
use std::mem;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let md = mem::ManuallyDrop::new(self);
        (md.value.a, md.value.b) // 编译错误:`value`是私有字段
    }
}

解决方案:ManuallyDrop 配合 unsafe 代码

目前Rust标准库中没有完全安全的API能实现这个需求,必须借助unsafe,但这种unsafe是可被证明安全的:通过ManuallyDrop抑制结构体的析构函数,再通过unsafe代码取出字段所有权,确保不会触发原结构体的析构逻辑,同时字段本身的析构函数会正常执行。

推荐的实现方式:

use std::mem::ManuallyDrop;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let mut md = ManuallyDrop::new(self);
        // 用ManuallyDrop::take取出字段,该unsafe操作是安全的:
        // 1. ManuallyDrop已抑制了D1的析构函数,不会访问已移出的字段
        // 2. 取出的String会在返回值销毁时正常执行自身的析构逻辑
        let a = unsafe { ManuallyDrop::take(&mut md.a) };
        let b = unsafe { ManuallyDrop::take(&mut md.b) };
        (a, b)
    }
}

或者通过指针直接读取字段:

use std::mem::ManuallyDrop;

impl D1 {
    fn into_raw_parts(self) -> (String, String) {
        let md = ManuallyDrop::new(self);
        // 指针读取是安全的,因为ManuallyDrop确保D1不会被析构
        let ptr = &md as *const D1;
        let (a, b) = unsafe { ptr.read_unaligned() };
        (a, b)
    }
}

安全依据

  • ManuallyDrop会阻止原结构体的drop方法被调用,避免了析构函数访问已移出字段的风险。
  • 所有字段的所有权被正确转移,它们的析构函数会在返回值被销毁时正常执行,没有内存泄漏或悬空引用问题。

内容的提问来源于stack exchange,提问作者ais523

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:45:46