Learning Rust 🦀: 16 - Rust Collections: Vectors
Tags: learning, rust, programming, beginners
Published: 2023-10-05 13:12:11

Now we will have a look at some of Rust's most common **Collections**, The **Vector**, **String** (yes! String type is a collection of characters 😎), and **Hash Map** types. We will start first with Cyborg mmm... I mean, Vector 😁
![cyborg](https://media.giphy.com/media/u2sdwCFtYDXBs07t8Y/giphy.gif)

>#### ⚠️ Remember!
You can find all the code snippets for this series in its [accompanying repo](https://github.com/FadyGrAb/rust-learning-series)  
If you don't want to install Rust locally, you can play with all the code of this series in the official [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021) that can be found on its official page.

> ⚠️⚠️ The articles in this series are loosely following the contents of "The Rust Programming Language, 2nd Edition" by Steve Klabnik and Carol Nichols in a way that reflects my understanding from a Python developer's perspective.

⭐ I try to publish a new article every week (maybe more if the Rust gods 🙌 are generous 😁) so stay tuned 😉. I'll be posting "new articles updates" on my [LinkedIn](https://www.linkedin.com/in/fady-abdelmassih) and [Twitter](https://twitter.com/fadyGrAb).

## Table of Contents:
- [Creating new Vectors](#creating-new-vectors)
- [Updating Vectors](#updating-vectors)
- [Reading Vectors elements](#reading-vectors-elements)
- [Using Enum to make a Vector contain multiple types](#using-enum-to-make-a-vector-contain-multiple-types)

### Creating new Vectors:
Before I show you how to create a new Vector, there is something you need to know. Unlike Arrays or Tuples that can hold a list of items themselves, Vectors are created in the **Heap**. Meaning, that *they don't have to be of known length at compile time!* But similar to Arrays and Tuples, they must contain elements of the same type.  
We can create an empty Vector as follows:
```rust
let v: Vec<i32> = Vec::new();
```
Because the compiler doesn't know the type of the elements of the newly created Vector, there type must be explicitly stated using `Vec<T>` (Yes, generics which we will talk about them later). So here, we are creating an empty Vector that will hold element of type `i32`.  
Also, we can create prefilled Vectors like this:
```rust
let v = vec![1, 2, 3];
```
By using the `vec!` macro, we are creating also a `Vec<i32>` Vector as `i32` is the default integer type.
> Vectors - like any other Rust type - are immutable by default meaning that you can't add or remove items from them. To make them mutable, we use the `mut` keyword.

### Updating Vectors:
The Vector type has a lot of useful methods that can update its elements. `push` and `pop` are very common in such operations. Consider the following example where we create an empty Vector and add 3 elements to it then remove one:
```rust
fn main() {
    let mut v: Vec<i32> = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    println!("Length of v is: {}", v.len());
    v.pop();
    println!("New length of v is: {}", v.len());
}
```
The first print will give you `Length of v is: 3` and the second one will print `New length of v is: 2`. `push` adds elements to the Vector. I like to imagine the Vector as a "stack" and `push` adds a new element each time at the top of the stack which means that the very first item that was added using `push` will be at the bottom of the stack. `pop` on the other hand, remove the top item from this stack.
> `len` is used to get the Vector "length" which in this case is the elements count.

### Reading Vectors elements:
You can access the Vector's elements using the square brackets "[ ]" similar to arrays (or Python's Lists and Tuples). We can also use the `get` method as follows:
```rust
fn main(){
    let v = vec![1, 2, 3];

    let first_element = &v[0];
    println!("First element is {first_element}");

    let second_element: Option<&i32> = v.get(1); 
    match second_element {
        Some(second) => println!("Second element is {second}"),
        None => println!("There isn't a second element!"),
    }
}
```
Vectors index start at 0 so `&v[0]` will return a reference to the first element in the Vector which in this case will be `1`. `get` returns an **Option<T>** type (remember the [Option type](https://dev.to/fadygrab/learning-rust-14-option-enum-an-enum-and-pattern-matching-use-case-1dgf)?) as the index passed to `get` can be outside of the Vector boundaries, then it will return the **None** variant of the **Option** type. Otherwise, it will return **Some(T)**. Here, `get(1)` should return a **Some(i32)** variant as the element at index `1` exists.  
Now let's see what will happen if we requested an "out of bound" element:
```rust
let v2 = vec![1, 2, 3];
let val = &v2[100];
```
This will compile just fine but will make the application "panic"! This behavior can be used if you want to crash the program if an out-of-bound value is entered. Now let's look at `get`:
```rust
let v2 = vec![1, 2, 3];
let val = v2.get(100); // Returns None
    if let None = val {
        println!("None is returned!")
    }
```
The application won't "panic" in this case and `None is returned!` will be printed out.
> `if let` is another way to do pattern matching that I mentioned [here](https://dev.to/fadygrab/learning-rust-14-option-enum-an-enum-and-pattern-matching-use-case-1dgf#if-let-alternate-way-to-pattern-matching)

One last thing before we leave this point, do you remember that we can't have both mutable and immutable references for a type in the same scope? This applies for Vector elements as well! Let's examine the following:
```rust
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);
println!("{first}");
```
This should compile just fine, right? We are using an immutable reference for the first item in the Vector and then adding a new item at the end of the vector, those should be unrelated, right? No, they are related and the code won't be compiled!  
Do you recall that Vectors are stored in the Heap? Which means that they can expand as they store their elements next to each other. And if the allocated memory to the Vector isn't large enough to hold all of its new elements, a new memory location will be assigned to it. Meaning that the `let first = &v[0];` will be referring to an invalid location in memory if the `v.push(4);` expression caused the Vector to be reallocated. Therefore, `prinln!("{first}");` will cause a compilation error!  

### Reading Vectors elements:
Similar to Arrays, we can iterate over a Vector like this:
```rust
let v = vec![1, 2, 3];
for i in &v {
    println!("{i}");
}
```
This will result in:
```
1
2
3
```
We can also change the values of the Vector's elements while iterating:
```rust
let mut v2 = vec![20, 40, 60];
for i in &mut v2 {
    *i -= 20;
}
println!("Modified vector:");
for i in &v2 {
    println!("{i}")
}
```
This will print:
```
0
20
40
```
> The "*" before `i` in the first `for` is called "dereference" operator which we will talk about in future articles.

### Using Enum to make a Vector contain multiple types:
Vectors are lists of elements of the same type but we can encapsulate different types with **Enum** in a custom type and use it with Victors! Let's see that in action:
```rust
#[derive(Debug)]
enum Item {
    Apple(u8),
    Banana(u8),
    Tape(f32),
    Book(String),
}

let shopping_cart = vec![
    Item::Apple(10),
    Item::Banana(5),
    Item::Book(String::from("Moby-Dick")),
    Item::Tape(5.5),
];
println!("Items in the shopping cart:");
for item in &shopping_cart {
    println!("{item:?}");
}
```
When running this code, we will have the following output:
```
Items in the shopping cart:
Apple(10)
Banana(5)
Book("Moby-Dick")
Tape(5.5)
```
Here we are building a `shopping_cart` Vector. But since Vectors can only have elements of the same type, we have created the `Item` **Enum** that has only 4 variants, *Apple* that takes their count, *Banna* that also takes their count, *Tape* that takes its length, and finally *Book* that takes its title. Then, we filled our `shopping_cart` Vector with variants of the **Item** enum which its variants encapsulate other data types and are technically of the same type. This technique will work if we know all the variants that we can use. But if we don't, the **Enum** won't work and we will have to use **Traits** which we will discuss later.

Vectors are the closest thing Rust has to Python's List's. They are relatively straight forward to create and use. Next, we will revisit another Rust Collection, The *String*! See you then 👋