Size_of_val based on slice definition

Hi folks,

Please help me understand why the first size_of_val returns 8 and the second one 16? a_s1 type is auto detected and a_s1 is user-typed.

fn main() {
    use std::mem;

    let a = [2, 3, 3, 3];

    let a_s1: &[i32; 4] = &a;
    println!("size of a_s1: {}", mem::size_of_val(&a_s1));

    for i in a_s1 {
        print!("{i} ")
    }
    println!("\n---");

    let a_s2: &[i32] = &a;
    println!("size of a_s2: {}", mem::size_of_val(&a_s2));

    for i in a_s2 {
        print!("{i} ")
    }
    println!("\n---");   
}

(Playground)

Output:

size of a_s1: 8
2 3 3 3 
---
size of a_s2: 16
2 3 3 3 
---

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.80s
     Running `target/debug/playground`

a_s1 is a reference to an array, a fixed size type. a_s2 is a reference to a slice, a dynamically sized type. References to fixed sized types are 1 pointer in size, while references to dynamically sized types are "fat", containing a pointer and additional data (in the case of a slice, the additional data is the length).

3 Likes

Thanks @jw013. So, the autodetection works as if it references the entire array?

Can you rephrase your question with more details / specifics and clarify what you mean by autodetection?

By default, if you type let a_s1 = &a;, rust autodetects the type of a_s1 as &[i32; 4] and I was thinking it will default to just &[i32].

Yes, because the type of a is array, &a will have type reference to array and the compiler will not do anything else to change that without a good reason.

1 Like

A great way to do this is to intentionally turbofish the methods incorrectly and look at the error messages. For example, if you make them both size_of_val::<i32>, the compiler says

error[E0308]: mismatched types
   --> src/main.rs:7:58
    |
7   |     println!("size of a_s1: {}", mem::size_of_val::<i32>(&a_s1));
    |                                  ----------------------- ^^^^^ expected `&i32`, found `&&[i32; 4]`
    |                                  |
    |                                  arguments to this function are incorrect
    |
    = note: expected reference `&i32`
               found reference `&&[i32; 4]`
note: function defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/mod.rs:342:14
    |
342 | pub const fn size_of_val<T: ?Sized>(val: &T) -> usize {
    |              ^^^^^^^^^^^

error[E0308]: mismatched types
   --> src/main.rs:15:58
    |
15  |     println!("size of a_s2: {}", mem::size_of_val::<i32>(&a_s2));
    |                                  ----------------------- ^^^^^ expected `&i32`, found `&&[i32]`
    |                                  |
    |                                  arguments to this function are incorrect
    |
    = note: expected reference `&i32`
               found reference `&&[i32]`
note: function defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/mod.rs:342:14
    |
342 | pub const fn size_of_val<T: ?Sized>(val: &T) -> usize {
    |              ^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.

And thus the difference is between passing a reference to a reference to an array vs passing a reference to a reference to a slice.