Search code examples
rustslicenotation

Is there any practical considerations to prefer one notation for converting vectors into slices?


A vector can be de-referenced into a slice by either of:

  • let slice = &*my_vec;
  • let slice = &my_vec[..];

I prefer the second, even though its more verbose, I find it more clear especially when the statement is mixed with densely used operators, and where de-referencing has different implications depending on Box/Vec/pointer types.

On the other hand, it uses a redundant range.

I'd like to ignore code-style personal preference and focus on tangible differences. Do they ever compile down to different code for release builds?


Solution

  • There is strictly no difference after optimizations:

    #[no_mangle]
    extern {
        fn simple(ptr: *const u8, len: usize) -> usize;
    }
    
    fn take_slice(slice: &[u8]) {
        unsafe { simple(slice.as_ptr(), slice.len()); }
    }
    
    #[inline(never)]
    fn take_vec_auto(v: &Vec<u8>) {
        take_slice(v);
    }
    
    #[inline(never)]
    fn take_vec_deref(v: &Vec<u8>) {
        take_slice(&*v);
    }
    
    #[inline(never)]
    fn take_vec_index(v: &Vec<u8>) {
        take_slice(&v[..]);
    }
    

    Leads to the following LLVM IR on the playground:

    ; Function Attrs: noinline nounwind uwtable
    define internal fastcc void @_ZN8rust_out13take_vec_auto17h2827abd8ce79beacE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
    entry-block:
      %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
      ret void
    }
    
    ; Function Attrs: noinline nounwind uwtable
    define internal fastcc void @_ZN8rust_out14take_vec_deref17h66cf4ce954b36d1dE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
    entry-block:
      %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
      ret void
    }
    
    ; Function Attrs: noinline nounwind uwtable
    define internal fastcc void @_ZN8rust_out14take_vec_index17h77571b14bbdb120cE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
    entry-block:
      %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
      ret void
    }
    

    So it is mostly a matter of style, and style is subjective.