Rust says the function parameter does not live enough, even though proper lifetime have been placed

188 views Asked by At

For background context: I am creating an Observer/Subscriber based Global Event System (using single shared event systems). I decided to use FnMut as my callback closure. The lifetime 'a placed at the impl of the makeshift struct Data<'a> should allow the callback parameter in the method mut_func() to live as long as the whole Data struct. Because the callback parameter uses the F generic which is definitely constrained by the lifetime 'a. But the error is still appearing stating that the callback parameter does not live long enough.

I originally used Box<T> as my container for the dyn FnMut(u32), but the Box<T> requires the callback to be 'static (because I am casting the boxed generic into a box trait objects) which in my scenario is not achievable (for readability). I then tried to use Rc<RefCell<T>> which sadly does not support trait objects.

Also, I am using generics for callback parameter because I want that function to have higher readability instead of having to see Box<T> wrapping around an entire closure that will be everywhere, since this Event System will be my central part of my program. I will do anything to make the 'front-end' more readable and cleaner (except for significant performance impact).

Note: This is my example program. I can post the actual program if needed.

Error:

error[E0597]: `callback` does not live long enough
>  | impl<'a> Data<'a> {  
>  |      -- lifetime `'a` defined here  
>  |     fn mut_func<F: FnMut(u32) -> () + 'a>(&mut self, mut callback: F) {  
>  |         self.o.push(&mut callback as &mut dyn FnMut(u32) -> ());  
>  |         ------------^^^^^^^^^^^^^------------------------------  
>  |         |           |  
>  |         |           borrowed value does not live long enough  
>  |         argument requires that `callback` is borrowed for `'a`  
>  |     }  
>  |     - `callback` dropped here while still borrowed  

Example:

use std::any::Any;
use std::mem;
use std::rc::Rc;
use std::cell::RefCell;

struct Event<'a> {
    obs: Vec<&'a mut dyn FnMut(u32) -> ()>,
}

impl<'a> Event<'a> {
    fn subscriber<F: FnMut(u32) -> () + 'a>(&mut self, mut callback: F) {
        self.o.push(&mut callback as &mut dyn FnMut(u32) -> ());
    }
}

fn main () {
    let mut e = Event {obs: Vec::new()};
    let x = 3;
    e.subscriber(|n| {x+n;});
}
1

There are 1 answers

3
Kevin Reid On BEST ANSWER

the callback parameter uses the F generic which is definitely constrained by the lifetime 'a. But the error is still appearing stating that the callback parameter does not live long enough.

callback lives long enough, but the problem is you're not storing the callback you received, you're storing it converted to a trait object (dyn) and that trait object's data has to be owned by something.

I originally used Box<T> as my container for the dyn FnMut(u32), but the Box<T> requires the callback to be 'static

No it doesn't. This compiles:

struct Event<'a> {
    obs: Vec<Box<dyn FnMut(u32) -> () + 'a>>,
}

impl<'a> Event<'a> {
    fn subscriber<F: FnMut(u32) -> () + 'a>(&mut self, callback: F) {
        self.obs.push(Box::new(callback));
    }
}

Then with one more change, your example will compile: define x before e so that x lives longer than e:

fn main() {
    let x = 3;
    let mut e = Event {obs: Vec::new()};
    e.subscriber(|n| {x+n;});
}

Rust Playground copy