Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic casting Vec to array

While trying different ways to send a Vec<u8> to a function that expects an &[u8], I made a "mistake" which actually works. I wrote some sample code to show this:

fn sum(v: &[u8]) -> u8 {
    let mut s = 0u8;
    for x in v.iter() {
        s = s + x;
    }
    return s;
}

fn main() {
    let myarr = [1u8, 2u8, 3u8];
    let myvec = vec![1u8, 2u8, 3u8];

    println!("{}", sum(&myarr));
    println!("{}", sum(&myvec));
}

I have a few questions in relation to this:

  • Why and how does this work?
  • Is there an automatic casting between these two types?
  • Does it incur any penalty or it is just taking the memory location of the underlying array of the vector?
  • Does this mean that for this type of usage (read only operations on an array of numbers) is better to use an array and not a vector as the API?
like image 321
Hernan Avatar asked Sep 25 '15 19:09

Hernan


People also ask

What is Vec rust?

Vector is a module in Rust that provides the container space to store values. It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity.


2 Answers

Let's get one thing out of the way: there are no arrays in your sum function. Rust has three related types, and you can search here on Stack Overflow or The Rust Programming Language for more information about them:

  1. Slices &[T].
  2. Vectors Vec<T>
  3. Arrays [T; n]

The v argument is a slice, which is simply a pointer to a chunk of data and the number of elements.

Why and how does this work? Is there an automatic casting between these two types?

The Deref trait is coming into play here. There's an implementation of this trait that looks like:

impl<T> Deref for Vec<T> {
    type Target = [T];
    fn deref(&self) -> &[T];
}

This means that any reference to a Vec<T> can act as a reference to a [T] and gains all of the methods from the target type. Deref is understood by the compiler, but any type is able to implement it, so it's only sort of special.

Does it incur any penalty or it is just taking the memory location of the underlying array of the vector?

This is a zero-cost transformation, which is why the compiler will do it for you. It's very unusual to have the compiler do anything for you that is expensive.

Does this mean that for this type of usage it is better to use an array and not a vector as the API?

Absolutely! 100% of the time you should accept a &[T] instead of a &Vec<T>. More things than just a Vec can provide a &[T], and arrays are an example of that.

like image 178
Shepmaster Avatar answered Sep 29 '22 08:09

Shepmaster


You're looking at the Deref trait in action. If you have a type T that implements this trait, and you call a function that takes an argument of type &U, then &T will call deref on the value to make it the right type. The Book has a chapter about it.

You can see Vec's implementation here It just copies the Vec's pointer and length fields into a new struct, which is almost free.

And you're right, it's better to write functions that take views rather than owning types. A function that takes a Vec<u8> can only be called with a Vec<u8>, but a function taking a &[u8] can be called with a Vec<u8>, a [u8; 500], or &[u8] from somewhere else. Similarly, functions should take &T rather than Box<T> when possible, or &str rather than String.

like image 45
Will Fischer Avatar answered Sep 29 '22 07:09

Will Fischer