I'm currently struggling to understand what exactly is Box<dyn ...> allocating?
Using std::mem::size_of, I always get the same result for any dynamic type as it is based on the type itself. Same for values if I provide the type of a closure, which gives the following result:
So I was wondering, first why is the second example not working as expected and then how much space a Box<dyn ...> will actually take in memory, and not just on the stack, if I provide it a very large closure. Where will the different parts of the data be stored, the parts being:
The function's content (instructions) which I suppose remain in the program's binary and won't require an allocation each time ;
The function's pointer to indicate which should be called (which I suppose is part of the Box type);
The informations required for dynamic dipatch (which I suppose is part of the Box type)
Thanks in advance for your help
EDIT: Re-reading this post I realize my goal isn't clear here ; I'm wondering if I can use Box with a very large closure, without it requiring a big heap allocation.
Yeah, so the size_of thing you are doing at the top of the post just measures the stack size of the Box, which is 16 because it is both a pointer and a vtable.
Additionally, it's completely correct that some closures have the size zero. This is because they compile down to a unique anonymous type whose call method has the contents of the closure. Of course, if you cast it to a function pointer, or have the closure capture a value, then it will no longer be size zero.
As for the size of an allocation of a Box<dyn Trait>, well it varies. It has the same size as the object you put inside it, so if you put a zero-sized closure into it, it doesn't allocate, but if the closure has captured a bunch of variables, then it could be larger.
So you're saying that for closures, as long as I use functions that don't capture anything from their environment, they'll always be compiled down to ZST and as such putting them into Box<dyn ...> won't require heap allocation, just stack allocation for storing the dynamic dispatch informations + the pointer?
It allocates a single contiguous buffer dynamically ("on the heap" if you prefer that terminology) for its wrapped value.
size_of::<Box<_>> is the size of the box, not the size of its heap buffer. The size_of function returns the size of its type parameter. Apart from necessarily being a compiler intrinsic, this function is not magic – it doesn't have special knowledge as to what potential heap buffers a type might allocate and point to. It only gives you the size of that type itself, which for a box is exactly one pointer (for Sized types) or two pointers (for dynamically-sized types).
If you are interested in the size of the contained type, ask for size_of::<T>() instead of size_of::<Box<T>>() for Sized types and size_of_val::<T>() for unsized types. If you are interested in approximate heap sizes, use the heapsize crate.
Correct.
Almost. A Box<dyn Trait> is made of two pointers:
one to the data (which points to no allocated memory if the type behind the trait is zero-sized);
and another one to the vtable required for dynamic dispatch.
In the case of a zero-sized closure, there's nothing to store for the data part, not even a function pointer. Calling closures is realized by means of the call_once(), call_mut(), etc. methods of the various Fn* traits, so the actual function pointer goes into the vtable part. This is also true when you don't use dynamic dispatch: unless you explicitly convert a closure to a function pointer, it will be statically dispatched and there isn't any function pointer business going on.
Finally, @Alice has already explained why some closures are zero-sized. If you capture variables, then you can indeed make non-zero-sized closures, in which case a very big closure can cause a very big heap allocation.
So this means that, as long as the closure doesn't capture its environment, I can make it as big as I want, wrapping it inside a Box<dyn Fn...> won't require any heap allocation, right?