I'm wondering why f2 and f3 in main() below need a type annotation to compile but f1 does not? Is there any way to avoid the requirement of the type annotation in this trait?
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 a u32.
But for hownlong is it borrowed? What lifetime 'r in &'r u32? Since there is no lifetime parameter, it assumes HRTB (see foo_unsugared): the input closure must be callable on an integer borrowed for a lifetime of any size. That is, it requires that F offer an infinite family of callable interfaces.
the f closure in main, however, takes an argument whose type is inferred: &x: ?. Since it returns a u32 after a destructuring &x pattern match, Rust infers that the input type is &'? u32. That is, a borrow of a u32 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 accepts f.
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