Raw pointer of array and pointer of slice in Rust

Advertisements

I’m trying to better understand Rust’s raw pointers, and I thought the following code would fail (sorry for possible UB) [Playground]:

fn main() {
    println!("size of * const u32 ->{}", std::mem::size_of::<* const u32>());
    println!("size of * const [u32;13] ->{}", std::mem::size_of::<* const [u32;13]>());
    println!("size of * const [u32] ->{}", std::mem::size_of::<* const [u32]>());
    let x = 1u32;
    let arr = [1,2,3,4,5,6,7,8, 9, 10, 11, 12, 13u32];
    let px: * const u32 = &x;
    let parr: * const [u32;13] = &arr;
    let slice: &[u32] = &arr;
    let pslice: * const [u32] = slice;
    unsafe { println!("px -> {:?}", px.as_ref()) };
    unsafe { println!("parr -> {:?}", parr.as_ref()) };
    unsafe { println!("pslice -> {:?}", pslice.as_ref()) };
    //
    println!("How could this work:");
    let pslice2: * const [u32] = parr; // How could this work ?
    unsafe { println!("pslice2 -> {:?}", pslice2.as_ref()) }; // How could this work ?
}

But it worked even with miri:

Standard Error

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.16s
     Running `/playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/playground`

Standard Output

size of * const u32 ->8
size of * const [u32;13] ->8
size of * const [u32] ->16
px -> Some(1)
parr -> Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
pslice -> Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
How could this work:
pslice2 -> Some([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])

I am not surprised with the fact that slice pointer is sized 16 while array pointer is sized 8, because the type does not include the size of the data. But why code let pslice2: * const [u32] = parr;, following let parr: * const [u32;13] = &arr;, is accepted and why pslice.as_ref() produce some data?

>Solution :

I guess it could seem weird to initialise pslice2, a fat pointer (address+size), from parr, a simple pointer (address only), because the size information missing in parr is necessary for pslice2.

But this size information is known by the compiler (as a constant, not stored anywhere in a variable); that’s why parr enables accessing all the elements in the array.
Then, during the initialisation of pslice2, the «missing» size information is simply provided by the compiler (the constant is stored inside the fat pointer).
Once it’s done, pslice2 is a fully functional fat pointer (address+size) which enables accessing all the elements in the array.

Leave a ReplyCancel reply