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

如何在Rust中销毁全局静态OnceCell内部的对象?

如何销毁Rust全局静态OnceCell中的实例

在以下Rust代码中,局部变量_ms对应的MyStruct实例会在作用域结束时自动调用Drop trait的drop方法,但全局静态OnceCell中存储的MyStruct实例却不会被销毁。请问该如何手动触发这个实例的销毁?

原始代码:

use tokio::sync::OnceCell;

struct MyStruct {}

impl MyStruct {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {})
    }
}

impl Drop for MyStruct {
    fn drop(&mut self) {
        println!("---&gt; 自动销毁.")
    }
}

static HOLDER: OnceCell<MyStruct> = OnceCell::const_new();

async fn g2() -> &'static MyStruct {
    HOLDER
        .get_or_init(|| async { MyStruct::new().unwrap() })
        .await
}

#[tokio::main]
async fn main() {
    println!("Hello, world!");

    // _ms 会自动销毁
    let _ms = MyStruct {};

    // 内部的MyStruct实例不会被销毁
    let _ins = g2().await;
}

对应的Cargo.toml配置:

[dependencies]
anyhow = "1.0.102"
tokio = {version = "1.52.0", features = ["full"]}

为什么静态OnceCell中的实例不会被销毁

Rust的静态变量生命周期贯穿整个程序运行周期,程序退出时操作系统会直接回收其占用的内存,但不会调用变量内部值的Drop方法。此外,tokio::sync::OnceCell设计上只允许初始化一次,并且没有提供取出内部值的接口,因此无法直接触发内部实例的销毁。

解决方案:使用带可取出值的容器替代OnceCell

如果需要手动控制全局实例的销毁,可以改用Mutex<Option<MyStruct>>这类容器,通过take方法取出内部值,从而触发Drop。以下是修改后的代码示例:

use tokio::sync::Mutex;

struct MyStruct {}

impl MyStruct {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {})
    }
}

impl Drop for MyStruct {
    fn drop(&mut self) {
        println!("---&gt; 自动销毁.")
    }
}

// 使用Mutex包裹Option,支持初始化和取出操作
static HOLDER: Mutex<Option<MyStruct>> = Mutex::const_new(None);

async fn get_instance() -> &'static MyStruct {
    let mut guard = HOLDER.lock().await;
    // 如果未初始化则创建实例
    let instance = guard.get_or_insert_with(|| MyStruct::new().unwrap());
    // 转换为'static引用,需确保后续不会在实例销毁后使用该引用
    unsafe { &*(instance as *const MyStruct) }
}

async fn destroy_instance() {
    let mut guard = HOLDER.lock().await;
    // take会取出Option中的值,触发Drop
    let _ = guard.take();
}

#[tokio::main]
async fn main() {
    println!("Hello, world!");

    let _ms = MyStruct {}; // 局部实例会自动销毁

    let ins = get_instance().await;
    println!("已获取全局实例");

    destroy_instance().await; // 手动销毁全局实例
    println!("全局实例已销毁");
}

注意事项

  • 代码中的unsafe用于将Mutex guard中的引用转换为'static,实际使用时必须确保:调用destroy_instance后,没有其他地方再持有之前获取的'static引用,否则会导致悬垂引用,引发未定义行为。
  • 这种方案更适用于测试环境下重置全局状态的场景;生产环境中频繁创建和销毁全局实例需要谨慎,避免引发并发问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 01:34:53