Search code examples
rust

Why does `std::mem::size_of::<Vec<[i32]>>()` fail to compile?


Consider the fact that the following code compiles and runs successfully:

println!("{}", std::mem::size_of::<Vec<i32>>()); // 24
println!("{}", std::mem::size_of::<Vec<char>>()); // 24
println!("{}", std::mem::size_of::<Vec<&str>>()); // 24
println!("{}", std::mem::size_of::<Box<i32>>()); // 8
println!("{}", std::mem::size_of::<Box<char>>()); // 8
println!("{}", std::mem::size_of::<Box<[i32]>>()); // 16
println!("{}", std::mem::size_of::<&[i32]>()); // 16
println!("{}", std::mem::size_of::<*mut *const [i32]>()); // 8

Now consider the following code:

println!("{}", std::mem::size_of::<[i32]>());

This fails to compile with error E0277 complaining that the size of [i32] is not known at compile time. I definitely see why this must be so, because [i32] is a i32 slice (i.e. a 'raw' slice in the sense given here [1], not a pointer to a slice (e.g. &[i32]), whose size is known at compile time (i.e. the size of a fat pointer = 2 * size_of(usize))).

But now consider the following code:

println!("{}", std::mem::size_of::<Vec<[i32]>>());

When you try to compile this code, you also get error E0277, complaining that the size of [i32] is not known at compile time.

Why is this the case? Consider again these lines from above:

println!("{}", std::mem::size_of::<Vec<i32>>()); // 24
println!("{}", std::mem::size_of::<Vec<char>>()); // 24
println!("{}", std::mem::size_of::<Vec<&str>>()); // 24

We clearly see that the size of Vec<T> is not dependent on the size of T, but is always the size a struct with 3 fields — pointer to data (8), length (8), capacity (8) — hence, 24.

So why does std::mem::size_of::<Vec<[i32]>>() not simply return 24, but instead fail to compile?

I know that Vec<T> requires T to implement Sized, and that that's the immediate reason. But I guess I'm more wondering about the deeper reason behind the design decision to make it the case that Vec<T> requires T to implement Sized.

Thanks!

[1] https://doc.rust-lang.org/reference/type-layout.html#slice-layout


Solution

  • But I guess I'm more wondering about the deeper reason behind the design decision to make it the case that Vec<T> requires T to implement Sized.

    It is very simple to answer if you think about how arrays work. After all, Vec is just a heap-allocated dynamically-sized array.

    Arrays allow random access (i.e. indexing), which is done by offsetting the pointer to the start of the array by the index times the size of the elements. If not all elements are the same size or if that size is not known, indexing just can not work. From that, it should be pretty clear why Vec<[i32]> (or indeed [[i32]; 4]) is a nonsensical type.

    (If indexing was not a requirement, it would be possible to just put slices one after another in memory, but it would be a pretty useless structure without knowing where each slice ends and the next one begins. That is better represented by a one-dimensional (flattened) array/slice, with a separately maintained list of starting indices. But at that point, you could just make an array of pointers to individual slices.)