Minimal repro:
fn foo (_: Option<impl FnOnce(&str)>)
{}
fn main ()
{
foo(Some(|_| ()));
}
Indeed, doing:
- foo(Some(|_| ()));
+ foo(Some(|_: &str| ()));
does fix the problem.
The issue stems from how closures, type inference, and higher-order signatures play together: for historical reasons, closures favor type inference / are reluctant to get higher-order promoted. In fact, they "rarely" do, except for one special hard-coded case which was added to have at least one way to get higher-order closures: when a literal closure expression is fed to a function which carries explicit Fn…
bounds on that very function arg.
It's one weird situation / quirk of the language which allows those bounds to "retroact" on the nature of the Fn…
-ness of the closure and tries to imbue it with a higher-order signature.
But in this minimal repro, as well as with ::rusqlite
's hooks
module feature-gated with an eponymous feature (cc @thomcc: that features is missing from the docs.rs batch and thus does not appear on the official docs), the closure parameter is not exactly an F
, but an Option<F>
, and the closure is thus given as a Some(…)
.
That seems to be enough to throw a wrench in the retroactive-higher-order-signature-imbuing works, at least when the type of the arg carrying the (to-be-higher-order) lifetime parameter is fully inferred. And it looks like providing a type annotation early (before type inference) that makes it obvious that there is a (promotable) lifetime parameter there does help nudge Rust into making the signature properly higher-order.
See also:
for more info and a helper macro to deal with more advanced closure signatures