Build Lists with Vec

RustBeginner
Practice Now

Introduction

A variable stores one value, while many programs need an ordered list whose length can change. Rust's Vec<T> is a growable sequence: every element has type T, values keep their order, and the vector owns its elements.

You will build a café order list, read one position safely, visit every order without consuming the list, and update every stored string through mutable references.

Create and Grow a Vector

In this step, you will add three owned strings to an initially empty vector.

Enter the prepared project and open its source:

cd /home/labex/project/cafe-orders
nano src/main.rs

The declaration Vec<String> means “a vector whose elements are owned String values.” This is the same container-style angle-bracket notation you saw in Option<u32>: here the contained element type is String. You will learn how to define such reusable types in the later generics Lab. Vec::new() creates the vector empty. The binding is mut because adding elements changes the vector.

Replace the Step 1 comment with three push calls:

    orders.push(String::from("Latte"));
    orders.push(String::from("Tea"));
    orders.push(String::from("Cake"));

push appends one element to the end, preserving insertion order. Save with Ctrl+O, press Enter, and exit with Ctrl+X. Check and run:

cargo check
cargo run --quiet

The output should be:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]

len() reports three elements. The :? placeholder uses Rust's debug formatting to display the complete vector, including quotes around its strings.

Read a Position Safely

In this step, you will read the second vector position without risking an out-of-bounds panic.

Vector positions start at zero, so index 1 refers to the second order. The get method returns an Option<&String>: Some contains a shared reference when the position exists, and None represents a missing position. The reference lets you read the element while the vector remains its owner.

Open the source:

nano src/main.rs

Replace the Step 2 comment with:

    match orders.get(1) {
        Some(order) => println!("Second order: {order}"),
        None => println!("Second order: missing"),
    }

This reads the element without moving its String out of the vector. Save and exit, then run:

cargo run --quiet

A new final line should appear:

Second order: Tea

The line proves both that position 1 exists and that insertion order was preserved.

Iterate with a Shared Reference

In this step, you will visit every order while keeping the vector available afterward.

Writing &orders borrows the vector instead of moving it into the for loop. Each loop value is therefore a shared reference to one stored String.

Open the source:

nano src/main.rs

Replace the Step 3 comment with this loop and follow-up check:

    for order in &orders {
        println!("Queued: {order}");
    }
    println!("Stored after display: {}", orders.len());

Save and exit, then run:

cargo run --quiet

The new lines should be:

Queued: Latte
Queued: Tea
Queued: Cake
Stored after display: 3

The final count is usable after the loop because the loop borrowed the vector and returned each shared borrow before continuing.

Update Through Mutable References

In this step, you will borrow each vector element mutably and update it in place before the display loop runs.

Only one mutable reference to a value may be active at a time. A for loop over &mut orders satisfies that rule by yielding one mutable element reference, using it for the current iteration, and then moving to the next element.

Open the source:

nano src/main.rs

Replace the Step 4 comment with:

    for order in &mut orders {
        order.push_str(" - ready");
    }

push_str appends borrowed text to each owned String. Rust automatically uses the mutable String behind each reference for this method call.

Save and exit, then check and run:

cargo check
cargo run --quiet

The complete output should be:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]
Second order: Tea
Queued: Latte - ready
Queued: Tea - ready
Queued: Cake - ready
Stored after display: 3

The updated queue lines prove that all elements changed in place. The final count proves the vector still owns the same three strings.

Summary

You created and grew a Vec<String>, accessed a position safely with get, iterated without consuming the vector, and updated every element through mutable references.