Problem
I made a user forum post about an issue I had related to this, but essentially, lifetime parameters in generic types (which don't really exist currently, and have to be specified using turbofish syntax by the caller IIRC) are not usable in higher order trait bounds, and worse, there is no way to override the default (inferred lifetime) manually (AFAIK). Below is an example of this behavior.
Example: playground
struct Wrapper<'a>(&'a str);
fn foo<F1, F2, T>(f1: F1, f2: F2, base: &'static str)
where
F1: for<'a> Fn(&'a str) -> T,
F2: Fn(T) -> bool
{
let owned = base.to_owned();
let str_ref = owned.as_ref();
let result = f1(str_ref);
assert!(f2(result));
}
fn dummy<'l>(s: &'l str) -> Wrapper<'l> {
Wrapper(s)
}
fn main() {
foo(dummy, |_| true, "test");
/*
error[E0271]: type mismatch resolving `for<'a> <for<'l> fn(&'l str) -> Wrapper<'l> {dummy} as std::ops::FnOnce<(&'a str,)>>::Output == _`
--> src/main.rs:19:5
|
3 | fn foo<F1, F2, T>(f1: F1, f2: F2, base: &'static str)
| ---
4 | where
5 | F1: for<'a> Fn(&'a str) -> T,
| - required by this bound in `foo`
...
19 | foo(dummy, |_| true, "test");
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime
*/
foo(dummy as for<'r> fn(&'r str) -> Wrapper<'r>, |_| true, "test");
/*
error[E0271]: type mismatch resolving `for<'a> <for<'r> fn(&'r str) -> Wrapper<'r> as std::ops::FnOnce<(&'a str,)>>::Output == _`
--> src/main.rs:33:5
|
3 | fn foo<F1, F2, T>(f1: F1, f2: F2, base: &'static str)
| ---
4 | where
5 | F1: for<'a> Fn(&'a str) -> T,
| - required by this bound in `foo`
...
33 | foo(dummy as for<'r> fn(&'r str) -> Wrapper<'r>, |_| true, "test");
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime
*/
foo::<_,_,Wrapper>(dummy, |_| true, "test");
/*
error[E0271]: type mismatch resolving `for<'a> <for<'l> fn(&'l str) -> Wrapper<'l> {dummy} as std::ops::FnOnce<(&'a str,)>>::Output == Wrapper<'_>`
--> src/main.rs:47:5
|
3 | fn foo<F1, F2, T>(f1: F1, f2: F2, base: &'static str)
| ---
4 | where
5 | F1: for<'a> Fn(&'a str) -> T,
| - required by this bound in `foo`
...
47 | foo::<_,_,Wrapper>(dummy, |_| true, "test");
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
*/
foo::<_,_,Wrapper>(dummy as for<'r> fn(&'r str) -> Wrapper<'r>, |_| true, "test");
/*
error[E0271]: type mismatch resolving `for<'a> <for<'r> fn(&'r str) -> Wrapper<'r> as std::ops::FnOnce<(&'a str,)>>::Output == Wrapper<'_>`
--> src/main.rs:61:5
|
3 | fn foo<F1, F2, T>(f1: F1, f2: F2, base: &'static str)
| ---
4 | where
5 | F1: for<'a> Fn(&'a str) -> T,
| - required by this bound in `foo`
...
61 | foo::<_,_,Wrapper>(dummy as for<'r> fn(&'r str) -> Wrapper<'r>, |_| true, "test");
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
*/
}
As far as I can tell, there is no way to specify the parameters to Wrapper
in the called function's higher order trait bounds, to match those specified in the dummy
function. In fact, there is no way to override the lifetime parameters to foo's T
with anything defined by a higher order trait bound. If I'm wrong please let me know.
Solution
There should be syntax and semantics (or better documentation if there is actually a way to do this) created to allow the binding of lifetime parameters in generics. I believe a good start would be syntax like T<'l>
allowed in generic parameters to signify types with lifetime parameters.
Example:
struct Wrapper<'a>(&'a str);
fn foo<F1, F2, T<'l>>(f1: F1, f2: F2, base: &'static str)
where
F1: for<'a> Fn(&'a str) -> T<'a>,
F2: Fn(T) -> bool
{
let owned = base.to_owned();
let str_ref = owned.as_ref();
let result = f1(str_ref);
assert!(f2(result));
}
...
Thoughts? Opinions?