Yes, you're drilling into the heart of it. Let's compare:
fn take_hr_closure< F: Fn(& I) -> O, I, O>(f: F) { let _ignored = f; }
fn take_closure<'a, F: Fn(&'a I) -> O, I: 'a, O>(f: F) { let _ignored = f; }
The first says that F must accept any lifetime [1] and produce the same output, O, for any lifetime. It can be called with any lifetime; the caller of take_hr_closure has no say in what lifetimes it will be called with. The lifetimes have no lower bound.
fn take_hr_closure<F: for<'any> Fn(&'any I) -> O, I, O>(f: F)
and F is a higher-ranked type.
The second says that F must accept one specific lifetime ('a) of the caller's choosing. The lifetime does have a lower bound: it's impossible for the caller of take_closure to choose a lifetime shorter than the call to take_closure.
These are very different things! Instead of ignoring the closures... let's use them!
fn take_hr_closure< F: Fn(& I) -> O, I: Default, O>(f: F) {
let i = I::default();
f(&i);
}
fn take_closure<'a, F: Fn(&'a I) -> O, I: Default + 'a, O>(f: F) {
let i = I::default();
f(&i);
}
The second does not compile because of the aforementioned lower bound. The contrast is even more stark for &mut / &'a mut.
Here's a comment that drove this home for me. It's a bit dense.
Here's an example with some brief explanation at the top. It was written before GAT stabilized, but in brief this pattern:
trait Indirect<'a> {
type Arg: 'a + FromValue<'a>;
}
// Or sometimes
trait Indirect<'a, _Lifetime_Guard = &'a Self> {
type Arg: 'a + FromValue<'a>;
}
// Elsewhere
where T: for<'a> Indirect<'a>
are GAT simulations of
trait Indirect {
type Arg<'a>: FromValue<'a>;
}
// Or
trait Indirect {
type Arg<'a>: FromValue<'a> where Self: 'a;
}
// Elsewhere
where T: Indirect
I have a cleaner playground version in my drafts somewhere.
that is well-formed, i.e., cannot be outside of
I's validity ↩︎