Deferred initialization and mutable static borrowing [Rust]

667 Views Asked by At

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.

0

There are 0 best solutions below