Start learning free

LESSON 98 OF 414 · ARRAYS I

Vec in Rust: The Growable Dynamic Array

Lesson Read the full explanation

A big limitation of static arrays in Rust is the fact that once the array is created, it has a fixed size.

The Vec overcomes this limitation by having variable size and allowing for elements to be added or removed.


Creation:

You declare a Vec by writing the type annotation with the element type inside <>, along with the name of the variable, and call Vec::new() to create an empty one:

let mut sandwich: Vec<String> = Vec::new();

This code creates an empty Vec named sandwich where each item is a String.


Initialization I:

You can also initialize a Vec at declaration by using the vec! macro with an opening bracket, end it with a closing bracket, and put a comma between each entry, like this:

let sandwich: Vec<&str> = vec!["peanut butter", "jelly", "bread"];

We created a Vec named sandwich which consists of 3 items: "peanut butter", "jelly" and "bread".


Initialization II:

Another way of initializing a Vec at declaration is by providing the number of elements and their initial value, like this:

let animals: Vec<&str> = vec!["cat"; 5];

We created a Vec named animals which consists of 5 "cat" items.


Assignment
Follow the Coding Tutorial and let's create some Vecs!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 2
    Ctrl + Enter to check

    RUST · ARRAYS I

    Arrays in Rust, explained

    Why Vec exists

    A static array has a length fixed at compile time: [i32; 4] always holds four integers. That is a problem when the number of items depends on the data, such as lines read from input or results collected in a loop. Rust's answer is Vec<T>, usually called a vector. It is a dynamic array: a sequence of values of one type T whose length can change while the program runs. A vector stores its elements in one contiguous block on the heap and takes care of growing that block when needed.

    Use an array when the size is known and constant, and a Vec in nearly every other case. Vectors are by far the most common collection in everyday Rust code.

    Creating a Vec

    An empty vector

    Write the type with the element type in angle brackets and call Vec::new():

    let mut sandwich: Vec<String> = Vec::new();
    

    This creates an empty vector whose items are Strings. The annotation is needed here because nothing yet tells Rust what the elements will be. Rust can also infer the type from later code, such as a first push. The mut keyword is needed to add or remove items afterwards, because bindings are immutable by default.

    With initial values: the vec! macro

    The vec! macro builds a vector from a list, like an array literal:

    let sandwich: Vec<&str> = vec!["peanut butter", "jelly", "bread"];
    

    The type is written out only for illustration. Rust infers it from the items, so let sandwich = vec!["peanut butter", "jelly", "bread"]; means the same. String literals have type &str, hence Vec<&str>.

    Repeating a value

    Like an array, a vector can be filled with one value repeated:

    let animals: Vec<&str> = vec!["cat"; 5];
    

    This is five copies of "cat". Unlike the array form [value; N], the count can be a variable computed at run time, as in vec![0; n].

    Working with a vector

    A vector knows its size, and a few methods cover the first things you'll do with it:

    • len() returns the number of items, as a usize.
    • push(x) adds an item at the end.
    • pop() removes the last item and returns it wrapped in an Option: Some(x), or None if the vector was empty.

    For a vector declared with let mut w: Vec<i32> = Vec::new();, after w.push(1); and w.push(2); the contents are [1, 2] and len() is 2. Then w.pop() returns Some(2) and leaves [1]. To print a vector, use the debug placeholder: println!("{:?}", w).

    The vector owns its items. Assigning it to another variable moves ownership, and clone() makes a separate copy.

    The lesson's exercise

    The exercise declares two vectors with the vec! macro.

    1. fruits holds the string literals "mango", "kiwi" and "grapefruit". Its type is Vec<&str>.
    2. ages holds the integers 14, 32, 50 and 21. Integer literals default to i32, so its type is Vec<i32>.
    let fruits = vec!["mango", "kiwi", "grapefruit"];
    let ages = vec![14, 32, 50, 21];
    

    Neither binding uses mut, because the exercise only creates the vectors. To push or remove items later, you would declare them with let mut.

    Common mistakes

    • Forgetting mut. Calling push on a binding declared without mut is a compile error. Add mut to the declaration.
    • Leaving out the !. vec[...] isn't valid. The macro is vec!, with the exclamation mark.
    • Mixing element types. vec![1, "two"] doesn't compile, because all elements must have the same type.
    • An empty vector with no type. let v = Vec::new(); with no later use that fixes the type produces a "type annotations needed" error. Annotate it, as in Vec<String> above.
    • Square brackets for the type. The element type goes in angle brackets: Vec<i32>, not Vec[i32].
    • Mixing up [x; n] and vec![x; n]. The first is a fixed array, the second a vector.

    Key takeaways

    • Vec<T> is a growable array of one element type.
    • Create one with Vec::new(), or with vec![...] and vec![value; count].
    • Declare it with let mut if you plan to change it.
    • Use len(), push and pop for the basics, and {:?} to print it.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free