Reputation: 998
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);
}
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