ais523
ais523

Reputation: 998

Why do I need an apparently useless type annotation when a closure matches against an enum-with-lifetime argument?

Here's a minimal reproduction for an issue I was having in a larger codebase:

enum A<'b> {
    A1(&'b u8)
}

fn consume_fnmut(_f: &mut impl FnMut (A)) {}
// Desugared:
// fn consume_fnmut<'a>(_f: &'a mut impl for<'b> FnMut (A<'b>)) {}

fn main() {
    let mut fnmut = |a| {
        match a {
            A::A1(_) => ()
        }
    };
    consume_fnmut(&mut fnmut);
}

Playground link

If I try to run this code, I get a "closure is not general enough" warning:

error: implementation of `FnMut` is not general enough
  --> src/main.rs:15:5
   |
15 |     consume_fnmut(&mut fnmut);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
   |
   = note: closure with signature `fn(A<'2>)` must implement `FnMut<(A<'1>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnMut<(A<'2>,)>`, for some specific lifetime `'2`

It looks like the Rust compiler has inferred my FnMut fnmut to take an argument of type A<'2> for some specific lifetime '2, which therefore isn't general enough to be passed to consume_fnmut (which requires the function referenced by its argument to implement for<'b> FnMut(A<'b>), i.e. to be able to work with arbitrary lifetimes).

I was able to fix this problem simply by explicitly specifying the type of the argument:

    let mut fnmut = |a: A| {
        match a {
            A::A1(_) => ()
        }
    };

Here, the a: A (with the lifetime 'b not mentioned at all) is a way to say "for all lifetimes 'b, a can be an A<'b>". The compiler accepts this code and it builds and runs without errors.

My question is: why is the type annotation needed in this context? From experimenting with minor variants of the problem, the cause seems to be the match statement – it is what causes the type of a to be inferred as A (because it is a pattern match against the variants of A), but I don't understand why it is inferring a specific lifetime '2 on A<'2>, rather than working with arbitrary lifetimes – it isn't even mentioning any of the fields of A::A1, so theoretically the lifetime should't matter!

Upvotes: 5

Views: 96

Answers (0)

Related Questions