My question is the following:
Would it be possible to make a deferred initialization of an object and then borrow it as mutable for a 'static
lifetime?
Some context
First of all, I am going to show an example of what I mean by deferred initialization. The deferred initialization model that I have come up with is to use the crate lazy_static
with a standard library Mutex
holding an Option<T>
. Here there is an example of a deferred initialization.
use lazy_static::lazy_static;
use std::thread::JoinHandle;
use std::sync::Mutex;
pub struct DeferredStruct {
internal_field: u32,
}
impl DeferredStruct {
pub fn new(internal_field: u32) -> Self {
Self {
internal_field,
}
}
pub fn regular_function(&mut self) {
self.internal_field += 1;
println!("{}", self.internal_field);
}
}
lazy_static! {
static ref MY_DEFERRED: Mutex<Option<DeferredStruct>> = Mutex::new(None);
}
fn main() {
// Initial processing
// ...
// ...
// The value 10 would be obtained from a configuration file on runtime.
let deferred_struct = DeferredStruct::new(10);
let mut lock = MY_DEFERRED.lock().unwrap();
lock.replace(deferred_struct);
std::mem::drop(lock); // In this example we drop the lock to avoid a deadlock when calling another_function.
// More processing
// ...
// ...
let thread_handle = another_function();
thread_handle.join().unwrap();
}
// From another part of the program and possibly from another thread we
// lock MY_DEFERRED and call regular_funcion on it.
fn another_function() -> JoinHandle<()> {
std::thread::spawn(|| {
let mut lock = MY_DEFERRED.lock().unwrap();
if let Some(deferred) = lock.as_mut() {
deferred.regular_function();
}
})
}
You can execute the above code in Rust Playground and check that it prints 11
correctly.
Introducing a struct function that requires a static lifetime
Now, let's say I add a function inside DeferredStruct
that will create a worker thread in order to execute some computations that will take a long time:
pub struct DeferredStruct {
internal_field: u32,
}
impl DeferredStruct {
pub fn new(internal_field: u32) -> Self {
Self {
internal_field,
}
}
pub fn regular_function(&mut self) {
self.internal_field += 1;
println!("{}", self.internal_field);
}
pub fn static_function(&'static mut self) {
std::thread::spawn(move || {
// Do something really long.
// Finally I make some changes on self
self.internal_field += 100;
});
}
}
In this case is required for &mut self
to have a 'static
lifetime:
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement.
Due to this, the function will take a &'static mut self
.
The problem comes when trying to borrow the DeferredStruct
inside the Option
in MY_DEFERRED
as 'static
and mut
.
If I cannot borrow for 'static
and mut
then I cannot call deferred.static_function()
. As the value will not live for long enough.
error[E0597]: `lock` does not live long enough
--> src/main.rs:57:33
|
57 | if let Some(deferred) = lock.as_mut() {
| ^^^^---------
| |
| borrowed value does not live long enough
| argument requires that `lock` is borrowed for `'static`
...
60 | })
| - `lock` dropped here while still borrowed
Here there is a minimal reproducible example in Rust Playground.
TL;DR
Is it possible to borrow an object created at runtime (not necessarily created at the immediate start of the program) inside a Mutex<Option<T>>
as mutable and for a 'static
lifetime?
Any help is appreciated.