The system here is:
- event: a trait to be "extended" for different types of events
- window: controller responsible for window creation and propagating its events further up the queue
- application: controller of the whole application which creates a window and makes other operations (not important for now).
The window and application objects must live as long as the program lives, as they ARE themselves the program.
I need to be able to create a callback function (a closure or a method) and pass it to the window struct with "set_event_callback" method, so it calls the handler when an event occurs.
However, I faced a problem of lifetimes, as the code below does not compile with an error:
error[E0521]: borrowed data escapes outside of method
--> src/main.rs:34:9
|
31 | pub fn run(&mut self) {
| ---------
| |
| `self` is a reference that is only valid in the method body
| let's call the lifetime of this reference `'1`
...
34 | / window.set_event_callback(|event| {
35 | | self.data += 1;
36 | | false
37 | | });
| | ^
| | |
| |__________`self` escapes the method body here
| argument requires that `'1` must outlive `'static`
trait Event {}
struct Window {
callback: Box<dyn FnMut(&mut dyn Event) -> bool>
}
impl Window {
pub fn new() -> Self {
Self {
callback: Box::new(|_| false)
}
}
pub fn set_event_callback<C>(&mut self, callback: C)
where C: FnMut(&mut dyn Event) -> bool + 'static {
self.callback = Box::new(callback);
}
}
struct Application {
data: i32
}
impl Application {
pub fn new() -> Self {
Self {
data: 0
}
}
pub fn run(&mut self) {
let mut window = Window::new();
window.set_event_callback(|event| {
self.data += 1;
false
});
}
}
fn main() {
Application::new().run();
}
I suspect this might have happened because the compiler does not know about the lifetimes of the window and application objects. However, I was not able to find a solution in a week.
Note: adding 'static to the &self in the run method (pub fn run(&'static self)) leads to another error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:42:5
|
42 | Application::new().run();
| ^^^^^^^^^^^^^^^^^^------
| |
| creates a temporary value which is freed while still in use
| argument requires that borrow lasts for `'static`
43 | }
| - temporary value is freed at the end of this statement
I've fixed your code just by adding explicit lifetime annotations.
I've promised the compiler that
callbackwill live no longer than the unique reference toapplicationit uses.But remember to generally avoid Rust lifetimes. It's just more mild incarnation of C++ pointer demon that is safer to deal with.
Yet they are often hard to write. There are very few use cases when they're the best tool to solve a problem.