0

I'm having trouble with Rust lifetimes. Here's a code sample:

use std::sync::Mutex

struct Bar<'a> {
    handler: Mutex<Option<Box<dyn 'a + Fn()>>>
}
 
impl<'a> Bar<'a> {
    fn new() -> Bar<'a> {            
        Bar {handler: Mutex::new(None)}
    }                                                                                                                                                                                         
    fn baz(&'a self) {
        let mut handler = self.handler.lock().unwrap();
        *handler = Some(Box::new(|| self.doprint()));
    }
    fn doprint(&self) {                                                                                                                                                                       
        println!("hello from Bar");
    }   
    fn runhandler(&self) {
        let handler = self.handler.lock().unwrap();
        if let Some(handler) = handler.as_ref() {
            handler();                                                                                                                                                                        
        }                                                                                                                                                                                     
    }
}
 
fn lala() {
    let bar = Bar::new();   
    bar.baz();   
}

The purpose is so it's possible to register a handler function, which is itself able to call methods of Bar. The handler's lifetime is set so that the function needs to exist as long as Bar does.

This produces the following error:

error[E0597]: `bar` does not live long enough
  --> src/chain/rpc_db.rs:68:5
   |
68 |     bar.baz();
   |     ^^^^^^^^^ borrowed value does not live long enough
69 | }
   | -
   | |
   | `bar` dropped here while still borrowed
   | borrow might be used here, when `bar` is dropped and runs the destructor for type `Bar<'_>`

I'm not sure why this error is happening given that the 'a lifetime is the lifetime of bar, so it should get dropped at the same time as bar. Although bar is borrowed for the baz call with an 'a lifetime, that call ends before the end of the function, so shouldn't interfere with the destructor. I know it's possible to solve this problem with Arcs but there's probably a better way.

Here's a more minimal example that fails with the same error:

use std::sync::Mutex

struct Bar<'a> {
    handler: Mutex<Option<Box<dyn 'a + Fn()>>>
}
 
impl<'a> Bar<'a> {
    fn new() -> Bar<'a> {            
        Bar {handler: Mutex::new(None)}
    }                                                                                                                                                                                         
    fn baz(&'a self) {
    }
}
 
fn lala() {
    let bar = Bar::new();   
    bar.baz();   
}
jessicata
  • 1
  • 1
  • The Rust compiler is guarding against the possibility that `dyn Fn() + 'a` accesses whatever `'a` is bound to whenever it is dropped, which is a problem if `'a` is bound to `self` (as done in `.bar()`) because `self` would already be dropped when the `dyn Fn()` is dropped. This is one reason why most formulations of `&'a Type<'a>` is wrong. Or looking at it from another angle, you are creating a nested object that references `self` a.k.a. a self-referential struct, which are likewise problematic. – kmdreko Sep 03 '22 at 19:25

0 Answers0