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:
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.
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:
&[T]
.Vec<T>
[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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With