Search code examples
rustlifetimeborrow-checker

How can I annotate the lifetimes for this Rust/Calloop callback code?


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:

  • what is "anonymous lifetime #2"?
  • is check_function<'a, F: 'a, ...> equivalent to check_function<'a, F, ...> where F: ... + 'a?

Solution

  • 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();
    }