Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:31:21
|
11 | ) -> std::result::Result<(), ()> {
| --------------------------- the `Output` of this `async fn`'s found opaque type
...
31 | Client::send_and_expect,
| ^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'r> fn(&mut Client, &'r M) -> impl futures::Future`
found fn pointer `for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future
I'm now completely confused about why in for<'r> fn(&mut Client, &'r M) -> impl futures::Future, &mut Client has no lifetime. And what does _ means in for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future`?
I'm very intersted in learning what's happening here.
I really researched about lifetimes some months ago, I tried finding everything I could, but they're still confusing to me
Both types are actually syntactically mis-represented. The second one is not even valid rust syntax, and the first one is not saying what it’s supposed to say. Let’s talk about lifetimes.
So, the for<…> thing related to higher-ranked lifetime bounds. In this case it’s actually about function types but they use the same syntax. A function type such as for<'a> fn(&'a mut Option<Foo>) -> Option<&'a mut Foo> is callable with references of any lifetime 'a and in this case, the return-type also depends on the choice of that lifetime. In terms of Fn-traits, this function pointer type satisfies the for<'a> Fn(&'a mut Option<Foo>) -> Option<&'a mut Foo> bound. Now, for function types, there’s actually a shorthand: Elided lifetimes in function pointer types and Fn-traits are standing for higher-ranked lifetimes:
In your connection_retrier, the f argument has type
fn send_and_expect<'a, 'b>(&'a mut self, m: &'b M) -> {some Future depending on 'a and 'b}
Now, usually the lifetime '_, also called “elided lifetime” is the same as writing no lifetime on references, in other words: &'_ M and &M mean the same. So lets talk about the weird types in the error messages:
for<'r> fn(&mut Client, &'r M) -> impl futures::Future
// and
for<'a, '_> fn(&'a mut Client, &M) -> impl futures::Future
They become less confusing (but not less wrong) if you write '_ for the elided lifetimes:
for<'r> fn(&'_ mut Client, &'r M) -> impl futures::Future
// and
for<'a, '_> fn(&'a mut Client, &'_ M) -> impl futures::Future
The problem is that something like
for<'r> fn(&'_ mut Client, &'r M) -> impl futures::Future
still stands for something like
for<'r> for<'a> fn(&'a mut Client, &'r M) -> impl futures::Future
but this is not what the compiler means. AFAICT, it just uses '_ like some concrete lifetime name in this error message. This is most clear by the fact that it writes for<'a, '_> which is invalid syntax. So let’s fix the error message’s types by choosing some actual legal lifetime names instead of '_:
for<'r> fn(&'e mut Client, &'r M) -> impl futures::Future
// and
for<'a, 'e> fn(&'a mut Client, &'e M) -> impl futures::Future
Now we can understand how the message relates to your code: The type
for<'r> fn(&'e mut Client, &'r M) -> impl futures::Future
corresponds to the type of the argument f of connection_retrier, the lifetime 'e is supposed to be the parameter 'a of the function.
The type
for<'a, 'e> fn(&'a mut Client, &'e M) -> impl futures::Future
corresponds to the function pointer type of the function send_and_expect.
Now, initially it seems strange that
for<'a, 'e> fn(&'a mut Client, &'e M) -> impl futures::Future
can’t be converted into
for<'r> fn(&'e mut Client, &'r M) -> impl futures::Future
since the former actually should be more general than the latter; not the other way around. The main problem is, AFAICT, something different that isn’t apparent from the error message at all. The problem I see is that send_and_expect has a type
for<'a, 'e> fn(&'a mut Client, &'e M) -> {future depending on 'a and 'e}
which can (I think) easily be converted/used as the type
for<'r> fn(&'a mut Client, &'r M) -> {future depending on 'a and 'r}
On the other hand, the argument f of type
for<'r> fn(&'a mut Self, &'r M) -> T
is supposed to return a future type T that might contain 'a (since that’s a lifetime parameter of the function) but it cannot depend on 'r!
The error message just writes impl Future for both things, regardless of what lifetimes they depend on, and hence hides the actual problem here.
Okay, now onto ways of fixing your error. Assuming that you only want to apply f to f_m in your connection_retrier, the higher-ranked type is entirely unnecessary. You can just write f: fn(&'a mut Self, &'a M) -> T.