How may I pass closure with self reference to foreign struct?

Dear Rustaceans,

Here's code snippet that I'd like to make it work. It doesn't compile, but I assume that it's enough to show what I want to do.

Code: Rust Playground

use log::{error, info};

// Note: Foreign library
#[derive(Default)]
struct Bar {
    client_id: Option<i32>,
    client_callback: Option<&'static dyn Fn() -> ()>
}

impl Bar {
    fn register(&mut self, id: i32, callback: &dyn Fn() -> ()) {
        match self.client_id {
            Some(_) => error!("Bar: already set"),
            None => {
                self.client_id = Some(id);
                self.client_callback = Some(callback);
            }
        };
    }
    
    fn notify(&self) {
        match self.client_callback {
            Some(callback) => callback(),
            None => error!("Bar: nothing to notify")
        }
    }
    
    fn unregister(&mut self, id: i32) {
        match self.client_id {
            Some(client_id) if client_id == id => {
                self.client_id = None;
                self.client_callback = None;
            }
            _ => error!("Bar: can't unregister"),
        }
    }
}


struct Foo {
    id: i32,
    callback: Option<Box<dyn Fn() -> ()>>
}

impl Foo {
    fn new(id: i32) -> Self {
        Self { id, callback: None }
    }

    fn connect(&mut self, bar: &mut Bar) {
        let callback = || self.respond();
        self.callback = Some(Box::new(callback));
        bar.register(self.id, &callback);
    }
    
    fn respond(&self) {
        info!("Foo: id={}", self.id);
    }
    
    fn disconnect(&mut self, bar: &mut Bar) {
        bar.unregister(self.id);
        self.callback = None;
    }
}

fn main() {
    let mut bar: Bar = Default::default();
    let mut foo: Foo = Foo::new(3);
    
    bar.notify();
    foo.connect(&mut bar);
    bar.notify();
    foo.disconnect(&mut bar);
    bar.notify();
}

Compilation error is as follows:

   Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
  --> src/main.rs:16:45
   |
11 |     fn register(&mut self, id: i32, callback: &dyn Fn() -> ()) {
   |                                               - let's call the lifetime of this reference `'1`
...
16 |                 self.client_callback = Some(callback);
   |                                             ^^^^^^^^ cast requires that `'1` must outlive `'static`

error[E0506]: cannot assign to `self.callback` because it is borrowed
  --> src/main.rs:52:9
   |
51 |         let callback = || self.respond();
   |                        -- ---- borrow occurs due to use in closure
   |                        |
   |                        `self.callback` is borrowed here
52 |         self.callback = Some(Box::new(callback));
   |         ^^^^^^^^^^^^^        ------------------ cast requires that `*self` is borrowed for `'static`
   |         |
   |         `self.callback` is assigned to here but it was already borrowed

error: lifetime may not live long enough
  --> src/main.rs:52:30
   |
50 |     fn connect(&mut self, bar: &mut Bar) {
   |                - let's call the lifetime of this reference `'1`
51 |         let callback = || self.respond();
52 |         self.callback = Some(Box::new(callback));
   |                              ^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0506`.
error: could not compile `playground` (bin "playground") due to 3 previous errors

I can understand the error -- Bar expects its callback to have static lifetime,
but Foo registers closure with Foo's reference, so Bar thinks that it may be dropped earlier.

But I don't know how to address the issue. Can someone advise me on this?

So two questions:
Question 1: How can I fix the build error? Can it be simply solved with specifying lifetime?

Question 2: Can I instantiate Foo's callback in the constructor? It doesn't change, so I feel like that there's a way to or pass Foo's method directly.

No, this is not possible. If the library really requires a 'static reference, then the referent (ie., the closure itself) must be valid for 'static, which means that it must not capture any short-lived references.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.