Store a Function Pointer with a Non-Static Lifetime in a Global Variable

Hi, I'm trying to store a function pointer in a Box<dyn FnMut(...)> inside a global variable. However, the function I want to store needs to reference another item that doesn't have a 'static lifetime.

Here's my goal:

I need to store a closure that can mutate state and references non-'static data. Then, this function should be callable from other parts of the program, ideally through a global/static container. And the function cannot be 'static because it needs to interact with data that has a shorter lifetime.

My initial approach was something like this:

use std::sync::{Mutex, MutexGuard};

struct State<'a> {
    val: Box<dyn FnMut(
        u64, u64
) -> i32 + 'a> 
}

impl<'a> State<'a> {
    fn new() -> Self {
        State {
            val: Box::new(add)
        }
    }   
}

static GLOBSTATE: Mutex<State> = Mutex::new(State::new());

pub fn add(a: u64, b:u64) -> i32 {
    return (a+b) as i32;
}

fn main() {
    let mut state = GLOBSTATE.lock().unwrap();
    let result = (state.val)(2, 3);
    println!("Result: {}", result);
}

But I quickly ran into issues because static requires 'static lifetimes, and my function must reference non-'staticdata.

I was thinking about alternatives such as storing directly in Vec / mutex, and they all require static lifetime.. I'm still not very confident when it comes to handling lifetimes properly in Rust. I feel like my current approach is running into fundamental issues due to Rust's strict ownership and lifetime rules. Is my idea feasible in Rust, or am I fundamentally approaching this the wrong way? If so, what would be a better pattern to achieve this?

Without knowing more about your use case, I'd say that Arc'ing your state to make sure it can live for as long as it's needed is the answer.

If you really need closures that capture borrows (are not 'static), then indeed, you cannot put them in a static variable.[1] Some solutions might be not using statics or might be not using borrowing closures. Another might be avoiding globals and passing around an Arc<Mutex<State<'_>>>[2] -- though I'm somewhat skeptical, as everything that tries to replace the state will have to agree on the lifetime in question.

A closure (or function) can interact with data that has a non-'static lifetime while being 'static itself by, for example, taking the data as an argument. fn(&str) works with any lifetime but satisfies a 'static bound.


  1. But your code example doesn't demonstrate that need. ↩︎

  2. or Rc<RefCell<State<'_>>> ↩︎

You might find it useful to use an ArcSwap: arc_swap - Rust

You can then pass this around directly or as part of a larger context object and you'll still be able to replace it when needed.

1 Like

Rust can't ensure safety of this.

In safe Rust, globals must never ever contain anything that is borrowed for a temporary lifetime. 'static bound means it's not temporary, and that restriction is there on purpose.

You can still store short lived potentially-dangling pointers in globals, but you need to use raw pointers *const T for this. You can't use safe references, because it actually is unsafe to work with global pointers to temporarily data.

Ideally you should find a solution that doesn't require global variables.

Or if you must have globals, make them own the data instead of borrowing. Copy or move the data to the global (using only types without lifetimes). If you use Box or Arc, it will still be a pointer, but this time Rust will be able to make it safe.

Also consider using thread_local globals. If you have temporary state to share, it's typically state on one thread. With true globals, you can have issues with threads overwriting each others data in the global.

1 Like