Why won't this typecheck?

trait Wrap {}
impl<T> Wrap for T where T: Fn(&()) {}
fn wants_wrapped_closure(_: Box<Wrap>) {}
fn main() {
    wants_wrapped_closure(Box::new(|_| ()));
}

This works, BTW.

fn wants_wrapped_closure(_: Box<Fn(&())>) {}

fn main() {
    wants_wrapped_closure(Box::new(|_| ()));
}

After a bit more poking around, it looks like the first example, where the function takes a unit reference but returns unit, can be fixed by specifying the type of the argument.

OTOH, if I take and return a unit reference, it will not work no matter what I do (sans eliminating the Wrap trait):

trait Wrap {}
impl<T> Wrap for T where T: Fn(&()) -> &() {}

fn wants_wrapped_closure(_: Box<Wrap>) {}

fn main() {
    wants_wrapped_closure(Box::new(|a: &()| -> &() { a }));
}

I suspect this is because technically, Fn can be implemented for more than one possible set of argument types, since Args is a type parameter of the trait and not an associated type.

Nope, should've looked at the playpen for the error.

This is a known limitation that you need to annotate the type of borrowed arguments in a closure for them to satisfy any HRTBs.

Known limitation: Confusing type error due to strange inferred type for a closure argument · Issue #41078 · rust-lang/rust · GitHub

2 Likes

As others have noted, this is a known bug unfortunately.

One workaround is to explicitly annotate the closure type using other means, e.g.

fn main() {
    fn annotate<F: Fn(&()) -> &()>(f: F) -> F { f }
    wants_wrapped_closure(Box::new(annotate(|a| a)));
}