Search code examples
ruststdallocationalloc

Why does `Allocator.allocate` hand out `NonNull<[u8]>`... but `deallocate` accepts `NonNull<u8>`?


As the title says.

pub unsafe trait Allocator {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
}

The two required methods on an Allocator both take different pointer types, this seems weird to me. Why has that choice been made?

Furthermore, why are u8 and [u8] chosen at all? Why not just have NonNull<()> or NonNull<[()]> for both methods?

My understanding is that choosing [u8] or u8 is a fine idea because it has the least-strict layout requirements that can actually be allocated (since Layout requires align to be non-zero). But, I think requiring a cast every time could be a better design choice.


Solution

  • The allocator might over-allocate, and NonNull<[u8]> is a fat pointer including length information.

    Why u8 rather than ()? Not sure, but it probably reflects the idea that all memory is a bunch of bytes in the end. Also the fact that pointers to ZSTs are allowed to dangle might have something to do with it.