使用不能声明为静态的函数在运行时初始化可变静态变量

Initializing a mutable static variable at runtime using a function which cannot be declared as static

use std::sync::Once;

struct Store;

impl Store {
    fn new() -> Store {
        Store {
            manifest: RwLock::new(/* ... */), // @note: RwLock does not implement Clone
        }
    }

    fn save(&mut self) {
        // ...
    }
}

失败代码:

static mut store: Option<Store> = None;
static INIT: Once = Once::new();

unsafe {
    INIT.call_once(|| {
        store = Some(Store::new());
    });
    store.unwrap().save(); // Error: cannot move out of static item `store`
}

错误很简单,但这是我迫切需要完成的事情,因为 Store::new() 在幕后做了大量计算量大的工作。

我要求看似不可能的事情:声明一个类型为 Store 的静态变量并在运行时对其进行初始化 – 考虑到 new 不能是 static 由于绝对关键的内部该函数内发生的可变性。此外,RwLock 是一项要求,无法克隆。我什至敢问这是否可以通过线程安全的方式完成?

没有延迟 new 函数调用或其内部属性,因为我程序中的后续函数调用(否则不相关)依赖于它。

为了使问题更清楚,这是在 FFI 的范围内。我对程序的生命周期或以更安全的方式管理此变量的能力没有太多控制权。虽然,我绝对希望这个 Store 变量在程序的整个生命周期内都存在!

我的问题是概念性的。我绝不期望这个确切的代码被稍微修改并突然编译。我在这里问一个问题,因为我无法全神贯注于如何实现我想要的。我尝试了其他几种方法...这是迄今为止我所做的最好的尝试。

如果没有 MCVE,很难确定你想要什么,但这是我设法做到的。

首先,正如编译器所述,您的问题非常简单:unwrap 消耗了 Option,而您不能消耗 static。但是,您真正想要的似乎是就地改变商店,这是可能的,AFAIK 甚至有几种方式。

下面是我选的。通常,要就地改变某些东西,我们必须获得对它的独占引用。根据 Option 的文档,有 as_mut method,它将 &mut Option<T> 转换为 Option<&mut T>。重要的是这个新选项是 owned,即它 not 存储在静态中(它引用静态),所以它 可以unwrap消耗,产生必要的&mut引用,我们可以调用save:

fn main() {
    unsafe {
        INIT.call_once(|| {
            store = Some(Store::new());
        });
        store.as_mut().unwrap().save();
    }
}

Playground 可能实现您的目标。


但是,我不太确定您对 static mut 的使用是否合理。首先,完全不清楚为什么你需要在 save 中使用 &mut self:如果你在 Store 中唯一拥有的是 RwLock,你只是共享引用很好,因为这就是锁定的意义所在。在这种情况下,您可以简单地将 Store (双关语意想不到的)存储在 lazy_static:

// skipping definitions

use lazy_static::lazy_static;

lazy_static! {
    static ref store: Store = Store::new();
}

fn main() {
    store.save();
}

Playground