Tuples in Rust

About This Module

This module covers Rust's tuple data structure, which allows grouping multiple values of different types into a single compound value. Tuples are fundamental for returning multiple values from functions and organizing related data.

Prework

Prework Readings

Read the following sections from "The Rust Programming Language" book:

Pre-lecture Reflections

Before class, consider these questions:

  1. What advantages do tuples provide over separate variables?
  2. How might tuples be useful for function return values?
  3. What are the trade-offs between tuples and structs?
  4. How does pattern matching with tuples improve code readability?
  5. When would you choose tuples versus arrays for grouping data?

Learning Objectives

By the end of this module, you should be able to:

  • Create and use tuples with different data types
  • Access tuple elements using indexing and destructuring
  • Apply pattern matching with tuples
  • Use tuples for multiple return values from functions
  • Understand when to use tuples versus other data structures
  • Work with nested tuples and complex tuple patterns

What Are Tuples?

A general-purpose data structure that can hold multiple values of different types.

  • Syntax: (value_1,value_2,value_3)
  • Type: (type_1,type_2,type_3)
#![allow(unused)]
fn main() {
let mut tuple = (1,1.1);
let mut tuple2: (i32,f64) = (1,1.1);  // type annotation is optional in this case

println!("tuple: {:?}, tuple2: {:?}", tuple, tuple2);
}
#![allow(unused)]
fn main() {
let another = ("abc","def","ghi");
println!("another: {:?}", another);
}
#![allow(unused)]
fn main() {
let yet_another: (u8,u32) = (255,4_000_000_000);
println!("yet_another: {:?}", yet_another);
}

Aside: Debug formatting

Look carefully at the variable formatting:

fn main() {
let student = ("Alice", 88.5, 92.0, 85.5);
println!("student: {:?}", student);
//                  ^^
}

Rust uses the {:?} format specifier to print the variable in a debug format.

We'll talk more about what this means, but for now, just know that's often a good tool to use when debugging.

Accessing Tuple Elements

There are two ways to access tuple elements:

1. Accessing elements via index (0 based)

#![allow(unused)]
fn main() {
let mut tuple = (1,1.1);
println!("({}, {})", tuple.0, tuple.1);

tuple.0 = 2;

println!("({}, {})",tuple.0,tuple.1);

println!("Tuple is {:?}", tuple);
}

2. Pattern matching and deconstructing

fn main() {
let tuple = (1,1.1);
let (a, b) = tuple;
println!("a = {}, b = {}",a,b);
}

Best Practices

When to Use Tuples:

  • Small, related data: 2-4 related values
  • Temporary grouping: Short-lived data combinations
  • Function returns: Multiple return values
  • Pattern matching: When destructuring is useful

When to Avoid Tuples:

  • Many elements: More than 4-5 elements becomes unwieldy
  • Complex data: When you need named fields for clarity
  • Long-term storage: When data structure will evolve

Style Guidelines:

// Good: Clear, concise
let (width, height) = get_dimensions();

// Good: Descriptive destructuring
let (min_temp, max_temp, avg_temp) = analyze_temperatures(&data);

// Avoid: Too many elements
// let config = (true, false, 42, 3.14, "test", 100, false);  // Hard to read

// Avoid: Unclear meaning
// let data = (42, 13);  // What do these numbers represent?

In-Class Exercise

Exercise: Student Grade Tracker

Create a program that tracks student information and calculates grade statistics. Work through the following steps:

  1. Create a tuple to store a student's name (String) and three test scores (f64, f64, f64)

  2. Calculate the average of the three test scores and create a new tuple that includes the student's name and average grade

  3. Use pattern matching to destructure and display the student's name and average in a readable format

  4. Bonus: Create multiple student tuples and use pattern matching to find students with averages above 85.0

fn main() {
    // Step 1: Create a student tuple (name, score1, score2, score3)
    let student1 = ...
    
    // Step 2: Deconstruct the tuple into separate variables
    let ...

    // Step 2: Calculate average and create new tuple (name, average)
    let average = ...
    let student_grade = ...
    
    // Step 3: Deconstruct student_grade into variables 
    // student_name and avg_grade
    let ...
    println!("Student: {}, Average: {:.1}", student_name, avg_grade);
    
}

Expected Output:

Student: Alice, Average: 88.7

Recap

  • Tuples are a general-purpose data structure that can hold multiple values of different types
  • We can access tuple elements via index or by pattern matching and deconstructing
  • Pattern matching is a powerful tool for working with tuples
  • Tuples are often used for multiple return values from functions