How does the compiler type boxed closure objects?


#1

Hello all,

I am trying to understand how the compiler works with boxed closure objects.

Basically, this works fine:

let firstClosureBox = Box::new(|| ());

Explicitly typing the variable works as well:

let secondClosureBox: Box<Fn()> = Box::new(|| ());

However, this doesn’t work:

let thirdClosureBox = Box::<Fn()>::new(|| ());

Because of:

/tmp/test_type.rs:8:27: 8:43 error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn()` [E0277]
/tmp/test_type.rs:8     let fifthClosureBox = Box::<Fn()>::new(|| ());
                                              ^~~~~~~~~~~~~~~~

If I understand correctly, this is because Box::new() is defined within impl<T>, not within impl<T: ?Sized>, and because Fn is not Sized.

But then, how and why does the rust compiler accept and encode secondClosureBox without issue?
Basically, I expected secondClosureBox and thirdClosureBox to be just as valid, but they are not, and I’m not exactly sure as to why that is.


#2

Each closure is a separate anonymous type, generated by compiler.


#3

secondClosureBox actually calls Box::<some_anonymous_closure_type>::new(|| ()) and coerces that Box<some_anonymous_closure_type> to Box<Fn()>. The reason this is necessary is that Box::new allocates memory and needs to know how much to allocate. Afterwards it’s perfectly fine to erase the type, as the deallocation doesn’t need to know how much to deallocate, just the address.