Here is a more minimal example, that illustrates the problem at hand:
fn foo<F> (_: F)
where
F : FnOnce(&u32) -> u32,
{}
fn bar<'r, F> (_: F)
where
F : FnOnce(&'r u32) -> u32,
{}
fn main ()
{
// infers a single but unknown lifetime
let f = |&x: /* &'? */ _| -> u32 {
x
};
foo(f); // fails: f must be callable with a borrow of any lifetime
bar(f); // ok
}
fn foo_unsugared<F> (_: F)
where
for<'r>
F : FnOnce(&'r u32) -> u32
,
{}
The problem lies with "type inference not leading to lifetime elision":
-
foo
expects a closure / callable taking a borrow of au32
.
But for hownlong is it borrowed? What lifetime'r
in&'r u32
? Since there is no lifetime parameter, it assumes HRTB (seefoo_unsugared
): the input closure must be callable on an integer borrowed for a lifetime of any size. That is, it requires thatF
offer an infinite family of callable interfaces. -
the
f
closure inmain
, however, takes an argument whose type is inferred:&x: ?
. Since it returns au32
after a destructuring&x
pattern match, Rust infers that the input type is&'? u32
. That is, a borrow of au32
with a single (but unknown / to-be-inferred) lifetime. -
it does thus not offer the infinite family of callable interfaces that
foo
expects from its input; hence the error.-
bar
, for instance, only requires (any) one callable interface, and thus acceptsf
.
-
Funnily enough, by making Rust start inferring the type of the parameter with a borrow "hint", & _
, the borrow part in the type is not inferred and does thus follow the classic elision rules, with an implicit universal lifetime parameter: it compiles just fine