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.