DaleSchool

Working with Multiple Values: Tuples, Arrays, and Vectors

Beginner15min

Learning Objectives

  • Group values of different types using tuples
  • Store fixed-length values of the same type using arrays
  • Create dynamically-sized lists using Vec
  • Iterate over vector elements with for

Working Code

Try running this code in the Rust Playground!

fn main() {
    // Tuple: can hold values of different types
    let person = ("Minsu", 25, true);
    println!("Name: {}", person.0);
    println!("Age: {}", person.1);

    // Array: same type, fixed size
    let colors = ["red", "green", "blue"];
    println!("First color: {}", colors[0]);

    // Vector: same type, can grow and shrink
    let mut fruits = vec!["apple", "banana", "grape"];
    fruits.push("strawberry");
    println!("Number of fruits: {}", fruits.len());

    for fruit in &fruits {
        println!("🍎 {fruit}");
    }
}

All three are ways to group multiple values together. Each one has a different use case!

Try It Yourself

  1. Use push to add 2 more of your favorite fruits to fruits.
  2. What happens if you try to access an index that doesn't exist, like colors[5]? Try running it and see.

"Why?" — The Difference Between the Three Collections

| | Tuple | Array | Vec | | ---------- | ------------------ | -------------- | ------------------ | | Types | Different types OK | Same type only | Same type only | | Size | Fixed | Fixed | Resizable | | Creation | (val1, val2) | [val1, val2] | vec![val1, val2] | | Access | .0, .1 | [index] | [index] | | Add/Remove | Not possible | Not possible | push(), pop() |

If you're not sure which one to use, think of it this way:

  • Tuple: When you want to bundle different kinds of values, like a name and an age
  • Array: When you have a fixed list that will never change (days of the week, color sets, etc.)
  • Vector: For almost everything else! This is the one you'll use the most in practice
fn main() {
    let mut scores = vec![90, 85, 77, 92];

    // Add an element
    scores.push(88);

    // Remove the last element
    let last = scores.pop(); // Some(88)
    println!("Popped value: {last:?}");

    // Iterate with for
    for score in &scores {
        println!("Score: {score}");
    }

    println!("Total of {} scores", scores.len());
}

Notice how pop() returns Some(88) instead of just 88? That's something called the Option type, which we'll cover later. For now, just think of it as a way to express "there might or might not be a value."

Deep Dive

Destructuring — Unpacking tuple values all at once

Besides using .0 and .1 to access tuple values, there's another way called destructuring.

fn main() {
    let person = ("Minsu", 25, true);

    // Destructure to give each value a name
    let (name, age, is_student) = person;
    println!("{name} is {age} years old");

    if is_student {
        println!("They're a student!");
    }
}

Much more readable than .0 and .1, right? Giving meaningful names to each value makes your code much cleaner.

Index Out of Bounds

If you try to access an index that doesn't exist in an array or vector, your program will panic.

fn main() {
    let numbers = [1, 2, 3];
    println!("{}", numbers[10]); // panic!
}

Error message:

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 10'

What this means: "The array has 3 elements, but you tried to access index 10." This isn't a compile-time error — it's a runtime error, meaning the program crashes while it's running. Always make sure your index is within bounds!

What Happens When You Pass a Vec to a Function

fn print_scores(scores: Vec<i32>) {
    for s in scores {
        println!("{s}");
    }
}

fn main() {
    let scores = vec![90, 85, 77];
    print_scores(scores);
    println!("Total of {} scores", scores.len()); // Error!
}

Error message:

error[E0382]: borrow of moved value: `scores`

Once you pass scores to a function, you can't use it anymore! For now, you can fix this by using .clone() to pass a copy instead.

fn print_scores(scores: Vec<i32>) {
    for s in scores {
        println!("{s}");
    }
}

fn main() {
    let scores = vec![90, 85, 77];
    print_scores(scores.clone()); // Pass a copy!
    println!("Total of {} scores", scores.len()); // OK!
}

We'll dig into why this happens later on. For now, just remember that .clone() makes a copy!

  1. Store 5 of your favorite foods in a vector, then use for to print them with numbers.
    • Hint: Create a counter variable with mut and increment it by 1 on each iteration.
    • Example output: 1. pizza, 2. tacos, ...
  2. Given the vector vec![10, 25, 30, 15, 40], write a program that iterates through it with for and prints only the values that are 30 or greater.

Q1. What is the value of colors.len() in the following code?

fn main() {
    let mut colors = vec!["red", "blue"];
    colors.push("green");
    colors.push("yellow");
    colors.pop();
    println!("{}", colors.len());
}
  • A) 2
  • B) 3
  • C) 4
  • D) Compile error

Q2. How do you access the second value in a tuple?

let data = (100, "hello", true);
  • A) data[1]
  • B) data.1
  • C) data.get(1)
  • D) data(1)

Q3. What is the biggest difference between an array and a vector?

  • A) Arrays can only store numbers, vectors can only store strings
  • B) Arrays have a fixed size, vectors can be resized
  • C) Arrays are fast, vectors are slow
  • D) Arrays use [] for access, vectors can only use .get()