0
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) {
        // ...
    }
}

Failing code:

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`
}

The error is straightforward, yet this is something I desperately need to accomplish because Store::new() does a ton of computationally expensive work under the hood.

I'm asking for the seemingly impossible: declare a static variable of type Store and initialize it at runtime – given that new cannot be static due to absolutely critical interior mutability that occurs within that function. Additionally, RwLock is a requirement and cannot be cloned. Dare I even ask whether this can be done in a way that is thread-safe?

There is no deferring the new function call or its internal properties as subsequent function calls in my program (which are otherwise irrelevant) rely on it.

To help make the problem more clear, this is within the context of FFI. I do not have much control over the program's lifetime or the ability to manage this variable in a safer way. Although, I definitely want this Store variable to live for the duration of the program's lifetime!

My question is conceptual. I am not by any means expecting this exact code to be slightly modified and suddenly compile. I'm asking a question here because I cannot wrap my head around how I can achieve what I want. I have tried several other methods... this is the best attempt I've made so far.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
jakenberg
  • 2,125
  • 20
  • 38
  • Am I understanding you correctly that you want to have a `static` variable which initialization is fairly complex? And you are fine with only running that initialization some time before the first access happens? *Or* do you require your static to be initialized before `main` runs? – Lukas Kalbertodt Feb 26 '20 at 08:40
  • @LukasKalbertodt there is no main, this is ffi. – jakenberg Feb 26 '20 at 15:59

1 Answers1

1

It's hard to be sure what do you want without an MCVE, but here is what I've managed to make.

First of all, your problem, as it is stated by the compiler, is quite straightforward: unwrap consumes the Option, and you can't consume the static. However, what do you really seem to want is to mutate the store in-place, and this is possible, AFAIK even in several ways.

The one I've chosen is following. In general, to mutate something in-place, we must get an exclusive reference to it. According to Option's docs, there is as_mut method, which converts &mut Option<T> into Option<&mut T>. The important part is that this new Option is owned, i.e. it is not stored in static (it references the static instead), and so it can be consumed by unwrap, yielding the necessary &mut reference, on which we can call save:

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

Playground with possible implementation of your goal.


However, I'm not quite sure if your usage of static mut is justified. First of all, it's entirely unclear why do you need to use &mut self in save: if the only thing you have in Store is the RwLock, you'd be just fine with the shared reference, because, well, that's the point of locking. And in this case you could simply store the Store (pun unintended) in lazy_static:

// skipping definitions

use lazy_static::lazy_static;

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

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

Playground

Cerberus
  • 8,879
  • 1
  • 25
  • 40
  • the first portion of your answer is what I needed. Specifically `as_mut`. As far as my justification for mutability... its out of my control due to a 3rd party library enforcing that requirement. – jakenberg Feb 26 '20 at 15:59