Vectors

Reviewed & published by Brayan K

Rust is a systems programming language focused on speed, memory safety, and fearless concurrency — and the vector, Vec<T>, is its standard growable list.

Part of the free Rust course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll create vectors, push and pop elements, index safely, and iterate over them by shared and mutable reference.

What You'll Learn in This Lesson

1️⃣ Creating and Accessing Vectors

You make a vector with Vec::new() and grow it with push, or build it directly with the vec! macro. Access elements either with [i] (fast, but panics out of range) or .get(i) (returns an Option, never panics).

fn main() {
    // A Vec<T> is a growable list of values of the same type, stored on the heap.

    // Create an empty vector and push onto it. Type is inferred from the pushes.
    let mut scores: Vec<i32> = Vec::new();
    scores.push(90);
    scores.push(85);
    scores.push(100);

    // The vec! macro builds a vector from a list — the common shortcut.
    let primes = vec![2, 3, 5, 7, 11];

    println!("scores: {:?}", scores);   // -> scores: [90, 85, 100]
    println!("primes: {:?}", primes);   // -> primes: [2, 3, 5, 7, 11]
    println!("count: {}", primes.len()); // -> count: 5

    // Indexing with [i] panics if out of range; .get(i) returns Option safely.
    println!("primes[0] = {}", primes[0]);            // -> 2
    println!("primes.get(99) = {:?}", primes.get(99)); // -> None
}

Notice .get(99) returned None instead of crashing — that's the safe way to access an index that might not exist, building on the Option you learned last lesson.

2️⃣ Iterating and Modifying

Loop over a vector with for n in &vec to read, or for n in &mut vec to modify — using *n to write through the mutable reference. Methods like pop, contains, and is_empty round out everyday use.

fn main() {
    let mut nums = vec![1, 2, 3, 4];

    // Iterate by reference to read each element.
    let mut sum = 0;
    for n in &nums {
        sum += n;
    }
    println!("sum = {}", sum);   // -> sum = 10

    // Iterate by MUTABLE reference to change elements in place.
    for n in &mut nums {
        *n *= 10;                // * dereferences to write through the reference
    }
    println!("scaled = {:?}", nums);   // -> scaled = [10, 20, 30, 40]

    // pop() removes and returns the last element as an Option.
    let last = nums.pop();
    println!("popped {:?}, now {:?}", last, nums);  // -> popped Some(40), now [10, 20, 30]

    // Useful methods:
    println!("contains 20? {}", nums.contains(&20));  // -> true
    println!("is empty? {}", nums.is_empty());        // -> false
}

The *n *= 10 line is the key detail: in a &mut loop each item is a reference, so you dereference with * to change the underlying value.

Your turn. Fill in the blanks marked ___, then run it.

// 🎯 YOUR TURN — finish the vector code, then run it.
fn main() {
    // 1) Build a vector of the numbers 5, 10, 15 using the vec! macro.
    let mut values = ___![5, 10, 15];    // 👉 the macro that builds a vector

    // 2) Push 20 onto the end.
    values.___(20);                      // 👉 the method that adds to the end
    println!("values = {:?}", values);   // expected: values = [5, 10, 15, 20]

    // 3) Sum the vector by iterating over a reference.
    let mut total = 0;
    for v in &values {
        total += v;
    }
    println!("total = {}", total);       // expected: total = 50
}

// ✅ Expected output:
//    values = [5, 10, 15, 20]
//    total = 50

Mini-Challenge: Simple Statistics

Compute the count, sum, and max of a vector. Run it with cargo run and check the output.

// 🎯 MINI-CHALLENGE: simple statistics.
//
// 1. Make a vector of i32: vec![4, 8, 15, 16, 23, 42].
// 2. Compute and print the count with .len().
// 3. Compute and print the sum (iterate over &vec and add).
// 4. Find and print the largest value. Hint: vec.iter().max() returns an
//    Option<&i32>; handle it with match or unwrap_or.
//
// ✅ Example output:
//    Count: 6
//    Sum: 108
//    Max: 42

fn main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Vectors

GoalRust Syntax
New emptylet mut v: Vec<i32> = Vec::new();
With valueslet v = vec![1, 2, 3];
Addv.push(4);
Remove lastv.pop();
Safe accessv.get(0)
Iteratefor x in &v { }

🎉 Lesson Complete!

Practice quiz

What is a Vec<T> in Rust?

  • A fixed-size array
  • A tuple
  • A growable list stored on the heap
  • A trait

Answer: A growable list stored on the heap. Vec<T> is a heap-allocated, resizable list of same-typed values.

Which method adds an element to the end of a vector?

  • .push(x)
  • .add(x)
  • .append_one(x)
  • .insert_end(x)

Answer: .push(x). push appends a single element to the end of the vector.

What does .pop() return?

  • The first element
  • The vector length
  • Nothing
  • An Option holding the last element

Answer: An Option holding the last element. pop removes and returns the last element as Some(value), or None if empty.

Which macro builds a vector with initial values?

  • Vec![1, 2, 3]
  • vec![1, 2, 3]
  • array![1, 2, 3]
  • list![1, 2, 3]

Answer: vec![1, 2, 3]. The vec! macro is the common shortcut for building a vector with values.

What happens when you index a vector out of range with v[i]?

  • The program panics
  • It returns None
  • It returns 0
  • It grows the vector

Answer: The program panics. Indexing with [i] panics if i is out of range.

What does v.get(99) return when index 99 does not exist?

  • Some(0)
  • A panic
  • None
  • The last element

Answer: None. .get(i) returns an Option, giving None safely instead of panicking.

In 'for n in &mut vec', why write *n to change an element?

  • * prints the value
  • n is a mutable reference; * writes through it
  • * removes the element
  • It is optional styling

Answer: n is a mutable reference; * writes through it. Each n is a &mut i32, so you dereference with * to assign to the underlying value.

What must a vector be declared as to push onto it?

  • let
  • const
  • static
  • let mut

Answer: let mut. Mutating or pushing requires the vector to be mutable (let mut).

Can a Vec<T> hold values of different types at once?

  • Yes, any types
  • No, all elements share type T
  • Only numbers
  • Only two types

Answer: No, all elements share type T. A Vec is single-type; use an enum or Vec<Box<dyn Trait>> for mixed data.

What does vec![4, 8, 15].iter().max() return?

  • 15
  • None
  • Some(&15)
  • Some(&4)

Answer: Some(&15). iter().max() returns an Option<&i32>, here Some(&15).

Continue this course

Frequently asked questions

What is the difference between an array and a Vec?

An array like [i32; 3] has a fixed length known at compile time and is usually stored on the stack. A Vec<T> is a growable list stored on the heap: you can push elements onto it and pop them off, and its length can change at runtime. Use an array when you know the exact number of elements up front and it will not change; use a Vec when the size is dynamic or unknown, which is the far more common situation.

When should I index with [i] versus .get(i)?

Indexing with vec[i] is concise but panics and crashes the program if i is out of range. The .get(i) method returns an Option: Some(&value) if the index is valid or None if it is out of range, letting you handle the missing case safely without a panic. Use [i] when you are certain the index is valid (for example inside a loop bounded by len), and use .get(i) when the index could be out of range.

Why do I need * when modifying elements in a for &mut loop?

When you iterate with 'for n in &mut vec', each n is a mutable reference (&mut i32), not the value itself. To change the value it points to, you dereference the reference with the * operator and assign through it, like '*n *= 10'. Without the *, you would be trying to reassign the reference rather than the value it refers to, which is not what you want and usually will not type-check.

Can a vector hold different types at once?

Not directly — a Vec<T> holds elements that are all the same type T. If you need to store values of different types in one vector, the common solution is to wrap them in an enum whose variants represent each type, and make a Vec of that enum. Trait objects (Vec<Box<dyn Trait>>) are another option when the values share behaviour. Both approaches give you one uniform element type while allowing varied underlying data.