06 Rustlings Vectors Solution
Vectors
Vectors are a widely-used data structure in Rust programming. In many programming languages, they might be referred to as Arrays. However, Rust operates at a lower level, which leads to a distinction between arrays and vectors. In Rust, an array is usually stored on the stack, which means it cannot grow or shrink, and its size must be determined at compile time. On the other hand, a vector is stored in the heap, allowing for greater flexibility as these size restrictions do not apply.
Let's look at these couple of exercises from Rustlings
Further information
vecs1.rs
// vecs1.rs
// Your task is to create a `Vec` which holds the exact same elements
// as in the array `a`.
// Make me compile and pass the test!
// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = // TODO: declare your vector here with the macro for vectors
(a, v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_and_vec_similarity() {
let (a, v) = array_and_vec();
assert_eq!(a, v[..]);
}
}
Our instructions are clear here in the comments create a vector that holds the elements [10, 20, 30, 40]
just like a
does. Simple enough.
For completeness let's also log what we are seeing as the errors.
vecs1.rs
errors
⚠️ Compiling of exercises/vecs/vecs1.rs failed! Please try again. Here's the output:
error: expected `;`, found `}`
--> exercises/vecs/vecs1.rs:13:11
|
13 | (a, v)
| ^ help: add `;` here
14 | }
| - unexpected token
error[E0425]: cannot find value `v` in this scope
--> exercises/vecs/vecs1.rs:13:9
|
13 | (a, v)
| ^ help: a local variable with a similar name exists: `a`
error[E0308]: mismatched types
--> exercises/vecs/vecs1.rs:9:23
|
9 | fn array_and_vec() -> ([i32; 4], Vec<i32>) {
| ------------- ^^^^^^^^^^^^^^^^^^^^ expected tuple, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
|
= note: expected tuple `([i32; 4], Vec<i32>)`
found unit type `()`
error: aborting due to 3 previous errors
We can see that the Rust compiler is a little confused and is suggesting ways to complete our code that are not what we are trying to do, but it's good to get a sense of what the compiler is doing in this situation so we can potentially understand other issues as we encounter them.
vecs1.rs
solution
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = vec![10, 20, 30, 40]; // using `vec!` macro to define a vector
(a, v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_and_vec_similarity() {
let (a, v) = array_and_vec();
assert_eq!(a, v[..]);
}
}
Completing the let v
line with vec![10, 20, 30, 40];
easily solves our exercise here.
Additional Notes
Remember that there is more than one way to create a vector
in Rust, we used the macro with vec!
because it's very convenient but we can also create vectors using the Vec::new()
function to create a new vector and then fill it with the push()
method.
Let's see what that would look like in this exercise:
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let mut v = Vec::new(); // using `Vec::new()` function remembering to make it `mut`
// placing elements into the array with `push()` method
v.push(10);
v.push(20);
v.push(30);
v.push(40);
(a, v)
}
This will also get our code to compile, but as you can see it's a lot more effort to do, so in this case it's much easier to use the Vec!
macro.
vecs2.rs
// vecs2.rs
// A Vec of even numbers is given. Your task is to complete the loop
// so that each number in the Vec is multiplied by 2.
//
// Make me pass the test!
//
// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for i in v.iter_mut() {
// TODO: Fill this up so that each element in the Vec `v` is
// multiplied by 2.
???
}
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
v
}
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|num| {
// TODO: Do the same thing as above - but instead of mutating the
// Vec, you can just return the new number!
???
}).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_loop() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_loop(v.clone());
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
#[test]
fn test_vec_map() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_map(&v);
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
}
Alright we have a more involved exercise here with 2 different areas that we need to write out. Our instructors are:
- Complete the loop so that each number in the Vec is multiplied by 2.
- First vector is mutable and the comments tell us that the vector we should have at the end of the calculations should be
[4, 8, 12, 16, 20]
- Our second vector is not mutable, so our instructions are to not mutate the vector but just return the new number.
vecs2.rs
⚠️ Compiling of exercises/vecs/vecs2.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
--> exercises/vecs/vecs2.rs:15:9
|
15 | ???
| ^ expected expression
error: expected expression, found `?`
--> exercises/vecs/vecs2.rs:26:9
|
26 | ???
| ^ expected expression
error: aborting due to 2 previous errors
These are the errors, pretty obvious as to why we are getting these.
Let's look the first loop and see how we complete it:
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for i in v.iter_mut() {
*i *= 2; // using the *i to access each element as well as `*=` to modify each element
}
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
v
}
It's a pretty simple solution, using *i *= 2
to multiply each element by 2, the main thing to remember is the use of the dereference *
operator when accessing each element and then *=
to modify or update each element with the new number. If we run the code now, we see that we're down to one error. So far so good.
⚠️ Compiling of exercises/vecs/vecs2.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
--> exercises/vecs/vecs2.rs:25:13
|
25 | ???
| ^ expected expression
error: aborting due to previous error
Now let's work on the 2nd for loop.
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|num| {
// TODO: Do the same thing as above - but instead of mutating the
// Vec, you can just return the new number!
???
}).collect()
}
Here we see the use of a map
and closure
which we haven't covered yet in our Rustlings journey, so let's just ignore those for now, but we have to complete the iterator but as they tell us in the comments, instead of mutating the vector just return a new number, so since we don't have to update each element and only multiply each element, we can simply use the multiplication *
operator and use the num
that's being passed in through the closure to do so. It would look something like this: num * 2
.
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|num| {
num * 2
}).collect()
}
For the vec_map
function, we use v.iter().map(|num| num * 2).collect()
. Here, num
represents each element in the vector, and num * 2
returns a new number that is twice the original value. We use collect()
to gather the resulting items into a collection, in this case, another vector. Notice the function signature vec_map(v: &Vec<i32>) -> Vec<i32>
, where v
is borrowed (&v
). This concept, known as borrowing, is fundamental to Rust's system of ownership.
vecs2.rs
solution
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for i in v.iter_mut() {
*i *= 2;
}
v
}
fn vec_map(v: &Vec<i32>) -> Vec<i32> {
v.iter().map(|num| num * 2).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_loop() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_loop(v.clone());
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
#[test]
fn test_vec_map() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_map(&v);
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
}
Here's the complete block of code, clean-up a bit for clarity without the comments etc. With this code we will pass our test and we can move on to the next set of exercises.
Conclusion
In this blog post, we explored Rust Vectors, a powerful and flexible data structure for handling dynamic arrays. We learned the difference between arrays and vectors in Rust and looked at some examples to understand how to create and manipulate vectors. We covered two different exercises: creating a vector from an array and performing operations on vector elements.
We learned how to create a vector using the vec!
macro and how to create a vector with the Vec::new()
function, and we also looked at the difference between mutable and immutable vectors. We discussed how to use loops and the map
function for iterating over vectors and performing operations on their elements.
By understanding and utilizing Rust vectors, you can create efficient and flexible programs that can handle a wide range of scenarios involving dynamic arrays.