Search code examples
pointersrustmemory-management

What is the difference between *mut [T] and *mut T when allocating?


I noticed that *mut [T] has a len() method. So it means that *mut [T] expects pointer to some memory location that contains both array length and the actual array, right? Therefore I cannot simply cast between the two types? Or is *mut [T] a fat pointer (so length is not stored behind the pointer but rather is part of the pointer)? Is this for example legal?

let l = Layout::array::<T>(size).unwrap();
let a = std::alloc::alloc(new_layout) as *mut [T];

Or should I use *mut T?


Solution

  • [T] is a dynamically sized type (DST). Pointers to DSTs are fat pointers that include the length, as you already suspected. Your example (simplified to use i32 instead of T)

    fn main() {
        let l = std::alloc::Layout::array::<i32>(10).unwrap();
        let a = std::alloc::alloc(l) as *mut [i32];
    }
    

    Demo

    will fail at compile time, with the following, very helpful error:

    error[E0607]: cannot cast thin pointer `*mut u8` to fat pointer `*mut [i32]`
     --> src/main.rs:5:9
      |
    5 | let a = std::alloc::alloc(l) as *mut [i32];
      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    For more information about this error, try `rustc --explain E0607`.
    

    Which neatly proves this point.


    An example to create a slice from a thin pointer could look like this:

    fn main() {
        let layout = std::alloc::Layout::array::<i32>(10).unwrap();
        let slice = unsafe { 
            let ptr = std::alloc::alloc(layout) as *mut i32;
            std::ptr::write_bytes(ptr, 0u8, 10); // initialize memory
            std::slice::from_raw_parts(ptr, 10) 
        };
        println!("{:?}", slice);
    }
    

    Demo