Thanatos
Thanatos

Reputation: 44256

How to solve this lifetime-related error?

Say I have the following,

type EpollEventCallback<'a> = FnMut(c_int) + Send + Sync + 'a;

struct EpollFdEventHandler<'a> {
    on_readable: Option<Box<EpollEventCallback<'a>>>,
    on_writable: Option<Box<EpollEventCallback<'a>>>,
}

// Map from c_int -> EpollFdEventHandler.
type EpollEventHandlerMap<'a> = collections::HashMap<c_int, EpollFdEventHandler<'a>>;

fn add_fd_handler
        <'a, T: Fn(bool, &'a mut EpollFdEventHandler<'a>)>(
        map: &'a mut EpollEventHandlerMap<'a>,
        fd: c_int,
        adder: T)
{
    let mut hash_entry: hash_map::Entry<'a, _, _> = map.entry(fd);
    match hash_entry {
        hash_map::Entry::Occupied(ref mut occ_e) => {
            let entry: &mut EpollFdEventHandler<'a> = occ_e.get_mut();
            adder(false, entry);
        },
        hash_map::Entry::Vacant(vac_e) => {
            /*
            adder(
                true,
                vac_e.insert(EpollFdEventHandler {
                    on_readable: None,
                    on_writable: None,
                }),
            );
            */
        }
    };
}

add_fd_handler is supposed to be a helper function for adding an "FD handler"; here, it's going to get passed a closure (adder) that will set either on_readable or on_writable, depending on which handler is being added. add_fd_handler's job is simply doing the hash table lookup, and inserting an empty entry if required. However:

src/event_loop.rs:85:35: 85:48 error: `(hash_entry:std::collections::hash::map::Occupied).0` does not live long enough
src/event_loop.rs:85         hash_map::Entry::Occupied(ref mut occ_e) => {
                                                       ^~~~~~~~~~~~~
src/event_loop.rs:82:1: 101:2 note: reference must be valid for the lifetime 'a as defined on the block at 82:0...
src/event_loop.rs:82 {
src/event_loop.rs:83     let mut hash_entry: hash_map::Entry<'a, _, _> = map.entry(fd);
src/event_loop.rs:84     match hash_entry {
src/event_loop.rs:85         hash_map::Entry::Occupied(ref mut occ_e) => {
src/event_loop.rs:86             let entry: &mut EpollFdEventHandler<'a> = occ_e.get_mut();
src/event_loop.rs:87             adder(false, entry);
                     ...
src/event_loop.rs:83:67: 101:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 83:66
src/event_loop.rs:83     let mut hash_entry: hash_map::Entry<'a, _, _> = map.entry(fd);
src/event_loop.rs:84     match hash_entry {
src/event_loop.rs:85         hash_map::Entry::Occupied(ref mut occ_e) => {
src/event_loop.rs:86             let entry: &mut EpollFdEventHandler<'a> = occ_e.get_mut();
src/event_loop.rs:87             adder(false, entry);
src/event_loop.rs:88         },

The error about occ_e only shows up if I try to use it with adder(false, entry)! Rust claims occ_e "does not live long enough", but it's only being used right there in that branch of the match, so how can that be?

My best guess presently is that the closure's second arg, as &'a mut is what's the issue here; my reference in occ_e isn't 'a, it's something shorter (the unspecified lifetime on hash_entry, I think, but I don't know how to notate that).

Upvotes: 0

Views: 88

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65752

Let the compiler infer the proper lifetime instead:

fn add_fd_handler
        <T: Fn(bool, &mut EpollFdEventHandler)>(
        map: &mut EpollEventHandlerMap,
        fd: c_int,
        adder: T)
{
    let mut hash_entry = map.entry(fd);
    match hash_entry {
        hash_map::Entry::Occupied(ref mut occ_e) => {
            let entry = occ_e.get_mut();
            adder(false, entry);
        },
        hash_map::Entry::Vacant(vac_e) => {
            /*
            adder(
                true,
                vac_e.insert(EpollFdEventHandler {
                    on_readable: None,
                    on_writable: None,
                }),
            );
            */
        }
    };
}

The problem is that you're letting the caller determine a lifetime for the callback, but you then invoke the callback with a mutable reference to a local variable. The caller couldn't possibly know about the lifetime of that local variable, so the compiler assumes that 'a must outlive the current function. Yet, entry does not outlive the function, which is why you get an error.

The declaration T: Fn(bool, &mut EpollFdEventHandler) is equivalent to T: for<'a, 'b> Fn(bool, &'a mut EpollFdEventHandler<'b>). The for keyword in this context allows you to declare that T must implement Fn for any value of the specified lifetime parameters. This is only valid for lifetime parameters, because different lifetime parameters do not cause multiple versions of a function to be defined, unlike for type parameters.

Upvotes: 1

Related Questions