I have a generic higher-order function in an external library that takes in a FnMut
, wraps it, and returns similar to this:
fn foo<T>(mut f:impl FnMut(T) -> T) -> impl FnMut(T)-> T{
move |t|f(t)
}
I decided to write a wrapper to handle a specific case:
fn bar(f:impl FnMut(&str) -> &str) -> impl FnMut(&str) -> &str{
foo(f)
}
However, the compiler errored out saying that one of the types wasn't general enough:
error[E0308]: mismatched types
--> src/main.rs:4:39
|
1 | fn foo<T>(mut f:impl FnMut(T) -> T) -> impl FnMut(T)-> T{
| -----------------
| |
| the expected opaque type
| the found opaque type
...
4 | fn bar(f:impl FnMut(&str) -> &str) -> impl FnMut(&str) -> &str{
| ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected associated type `<impl FnMut<(&str,)> as FnOnce<(&str,)>>::Output`
found associated type `<impl FnMut<(&str,)> as FnOnce<(&str,)>>::Output`
It compiles perfectly fine if I use a type without lifetimes like i32
, and writing out the HRTBs doesn't change the error. Best I can tell, the compiler is having trouble matching the lifetimes of the function outputs, but I can't figure out why it's having trouble and how to work around it.
CodePudding user response:
Unfortunately, rust doesn't currently allow you to create a general enough foo
for your bar
example.
But if you modify bar
so that the caller chooses the lifetime of f
, instead of it being generic over all lifetimes, you can make it work with the current foo
:
fn foo<T>(mut f: impl FnMut(T) -> T) -> impl FnMut(T)-> T {
move |t| f(t)
}
fn bar<'a>(f: impl FnMut(&'a str) -> &'a str) -> impl FnMut(&'a str) -> &'a str {
foo(f)
}
This is technically more restrictive, but for most use cases, it should be equivalent.