I have the following code
use std::future::Future;
fn main() {
handle(Test::my_func);
}
fn handle<Fut>(fun: for<'r> fn(&'r mut Test) -> Fut) -> bool
where
Fut: Future<Output = ()>,
{
true
}
struct Test {}
impl Test {
pub async fn my_func<'r>(&'r mut self) -> () {
()
}
}
Also, you can run it online on Rust Playground.
The following error appears:
error[E0308]: mismatched types
--> src/main.rs:4:12
|
4 | handle(Test::my_func);
| ^^^^^^^^^^^^^ one type is more general than the other
...
17 | pub async fn my_func<'r>(&'r mut self) -> () {
| -- checked the `Output` of this `async fn`, found opaque type
|
= note: while checking the return type of the `async fn`
= note: expected fn pointer `for<'r> fn(&'r mut Test) -> impl Future`
found fn pointer `for<'r> fn(&'r mut Test) -> impl Future`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`
To learn more, run the command again with --verbose.
Now, this is really weird because it clearly says that it's getting what it's expecting. I don't understand which type is more general than which. I don't understand lifetimes enough to debug this code. Could someone spare some more insight into this?
Sometimes you can get a better error from the nightly compiler:
16 | pub async fn my_func<'r>(&'r mut self) -> () {
| ^^ checked the `Output` of this `async fn`, found opaque type
= note: expected fn pointer `for<'r> fn(&'r mut Test) -> impl Future`
found fn pointer `for<'r> fn(&'r mut Test) -> impl for<'r> Future`
In simple terms, this means that you expect fun
to be a function which returns a struct implementing the Future
trait, but with an implicit + 'static
bound, meaning the struct cannot have fields which are only alive as long as 'r
.
However, Test::my_func
does want to refer to &mut 'r self
, and because of that the program is rejected by the complier.
You could imagine something like for<'r> fn(&'r mut Test) -> (impl Future + 'r)
, but this is currently not something that rustc
accepts, and AFAIK there isn't any way to do this (without unsafe or boxing everything).
Depending on your use case, you might be able to get away with:
fn handle<'a, Fut>(fun: impl Fn(&'a mut Test) -> Fut) -> bool
where
Fut: Future<Output = ()> + 'a,
{
true
}
this works because we require a single lifetime ('a
), which we can name for the Fut: Future<Output = ()> + 'a
bound, instead of any lifetime (for<'r> ..
) which we cannot name in that bound,
but this is a lot more restrictive.