Why does Box<[T]> need 16 bytes in memory, but a referenced slice needs only 8? (on x64 machine)
Box<T>
is basically *const T
(Actually it's a newtype around Unique<T>
, which itself is a NonNull<T>
with PhantomData<T>
(for dropck), but let's stick to *const T
for simplicity).
A pointer in Rust normally has the same size as size_of::<usize>()
except when T
is a dynamically sized type (DST). Currently, a Box<DST>
is 2 * size_of::<usize>()
in size (the exact representation is not stable at the time of writing). A pointer to a DST is called FatPtr
.
Currently, there are two kinds of DSTs: Slices and traits. A FatPtr
to a slice is defined like this:
#[repr(C)]
struct FatPtr<T> {
data: *const T,
len: usize,
}
Note: For a trait pointer, len
is replaced by a pointer to the vtable
.
With those information, your question can be answered:
Box<i8>
:i8
is a sized type => basically the same as*const i8
=> 8 bytes in size (with 64 bit pointer width)Box<[i8]>
:[i8]
is a DST => basically the same asFatPtr<i8>
=> 16 bytes in size (with 64 bit pointer width)Box<&[i8]>
:&[i8]
is not a DST. It's basically the same as*const FatPtr<i8>
=> 8 bytes in size (with 64 bit pointer width)
The size of a reference depends on the "sizedness" of the referenced type:
- A reference to a sized type is a single pointer to the memory address.
A reference to an unsized type is a pointer to the memory and the size of the pointed datum. That's what is called a fat pointer:
#[repr(C)] struct FatPtr<T> { data: *const T, len: usize, }
A Box
is a special kind of pointer that points to the heap, but it is still a pointer.
Knowing that, you understand that:
Box<i8>
is 8 bytes becausei8
is sized,Box<&[i8]>
is 8 bytes because a reference is sized,Box<[i8]>
is 16 bytes because a slice is unsized.