Why won't this typecheck?


#1

https://play.rust-lang.org/?gist=b5c4c41914657c086eb9d29a620c56e2&version=stable

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

#2

This works, BTW.

https://play.rust-lang.org/?gist=fd0b30e0eda18db61dc4bd70ea4e7d10&version=stable

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

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

#3

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 }));
}

#4

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.


#5

Known limitation: https://github.com/rust-lang/rust/issues/41078


#6

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)));
}