I am trying to write a function that adds a callback function to Calloop's event loop. The code works fine if it is simply pasted into a function where the function parameters are all in scope and on the stack. Pulling it out into a function results in lifetime problems.
This is as much as I could simplify the code before having to get into Calloop's internals (which are a bit beyond me as a beginner):
use calloop::{Interest, Mode, generic::Generic, LoopHandle};
use std::os::unix::io::RawFd;
fn check_function<F, T>(
handle: &mut LoopHandle<T>,
fd: RawFd,
callback: F,
)
where
F: FnMut(calloop::Readiness, &mut calloop::generic::Fd, &mut T) -> Result<(), std::io::Error>
{
let event_source = Generic::from_fd(fd, Interest::READ, Mode::Edge);
handle.insert_source(
event_source,
callback,
).ok();
}
Compiling this results in:
error[E0311]: the parameter type `F` may not live long enough
--> src/test.rs:14:12
|
4 | fn check_function<F, T>(
| - help: consider adding an explicit lifetime bound...: `F: 'a`
...
14 | handle.insert_source(
| ^^^^^^^^^^^^^
|
note: the parameter type `F` must be valid for the anonymous lifetime #2 defined on the function body at 4:1...
--> src/test.rs:4:1
|
4 | / fn check_function<F, T>(
5 | | handle: &mut LoopHandle<T>,
6 | | fd: RawFd,
7 | | callback: F,
8 | | )
9 | | where
10 | | F: FnMut(calloop::Readiness, &mut calloop::generic::Fd, &mut T) -> Result<(), std::io::Error>
| |_________________________________________________________________________________________________^
note: ...so that the type `F` will meet its required lifetime bounds
--> src/test.rs:14:12
|
14 | handle.insert_source(
| ^^^^^^^^^^^^^
Since it works when everything is on the stack, I figure I need to tell it that everything has the same (or compatible) lifetimes:
fn check_function<'a, F, T>(
handle: &mut LoopHandle<T>,
fd: RawFd,
callback: F,
)
where
T: 'a,
F: FnMut(calloop::Readiness, &mut calloop::generic::Fd, &mut T) -> Result<(), std::io::Error> + 'a
But rustc tells me to add another lifetime:
error[E0311]: the parameter type `F` may not live long enough
--> src/test.rs:15:12
|
4 | fn check_function<'a, F, T>(
| - help: consider adding an explicit lifetime bound...: `F: 'b`
...
15 | handle.insert_source(
| ^^^^^^^^^^^^^
|
note: the parameter type `F` must be valid for the anonymous lifetime #2 defined on the function body at 4:1...
--> src/test.rs:4:1
|
4 | / fn check_function<'a, F, T>(
5 | | handle: &mut LoopHandle<T>,
6 | | fd: RawFd,
7 | | callback: F,
... |
10 | | T: 'a,
11 | | F: FnMut(calloop::Readiness, &mut calloop::generic::Fd, &mut T) -> Result<(), std::io::Error> + 'a
| |______________________________________________________________________________________________________^
note: ...so that the type `F` will meet its required lifetime bounds
--> src/test.rs:15:12
|
15 | handle.insert_source(
| ^^^^^^^^^^^^^
This seems like a rather slow way to learn the alphabet. Rather than just throw lifetime annotations around until it works, I'd really like to understand how I can tell rustc that these lifetimes are all compatible. Secondary to that:
check_function<'a, F: 'a, ...>
equivalent to check_function<'a, F, ...> where F: ... + 'a
?Rustc wasn't being very helpful with this, but what I found is that LoopHandle
is parametrized with a lifetime, and since the handle takes ownership of the passed in FnMut
the FnMut
must outlive the LoopHandle
. Once that's figured out, annotating the correct lifetime is pretty easy:
use calloop::{
generic::{Fd, Generic},
Interest, LoopHandle, Mode,
};
use std::io::Result;
use std::os::unix::io::RawFd;
fn check_function<'a: 'b, 'b, F, T>(handle: &mut LoopHandle<'b, T>, fd: RawFd, callback: F)
where
F: 'a + FnMut(calloop::Readiness, &mut Fd, &mut T) -> Result<()>,
{
let event_source = Generic::from_fd(fd, Interest::READ, Mode::Edge);
handle.insert_source(event_source, callback).ok();
}