Rust Tuples
A tuple is a compound data type in Rust that allows you to group together a number of values with potentially different types into one compound type. Tuples have a fixed length, meaning once they're declared, they cannot grow or shrink in size.
Tuple Basics
Creating Tuples
Tuples are created by writing a comma-separated list of values inside parentheses:
fn main() {
// A tuple with different types
let person: (String, i32, f64) = (String::from("Alice"), 30, 5.8);
// Type inference works too
let coordinates = (10, 20); // (i32, i32)
// Empty tuple (unit)
let unit = ();
}
Accessing Tuple Elements
You can access tuple elements in two ways:
1. Using dot notation with index:
fn main() {
let person = ("Alice", 30, 5.8);
let name = person.0;
let age = person.1;
let height = person.2;
println!("Name: {}, Age: {}, Height: {} ft", name, age, height);
}
2. Using destructuring (pattern matching):
fn main() {
let person = ("Alice", 30, 5.8);
// Destructuring the tuple
let (name, age, height) = person;
println!("Name: {}, Age: {}, Height: {} ft", name, age, height);
}
Practical Examples of Tuples
Example 1: Returning multiple values from a function
Tuples are perfect for returning multiple values from a function:
fn calculate_statistics(numbers: &[i32]) -> (i32, i32, i32) {
let sum = numbers.iter().sum();
let max = *numbers.iter().max().unwrap_or(&0);
let min = *numbers.iter().min().unwrap_or(&0);
(sum, max, min)
}
fn main() {
let numbers = [1, 5, 10, 2, 8];
// Get multiple return values using a tuple
let (sum, max, min) = calculate_statistics(&numbers);
println!("Sum: {}, Max: {}, Min: {}", sum, max, min);
}
Example 2: Representing a point in 2D/3D space
Tuples are useful for representing coordinates:
fn main() {
// 2D point
let point_2d = (4.0, 3.0);
// Calculate distance from origin
let distance = (point_2d.0.powi(2) + point_2d.1.powi(2)).sqrt();
println!("Distance from origin: {}", distance);
// 3D point
let point_3d = (4.0, 3.0, 7.0);
println!("3D point: ({}, {}, {})", point_3d.0, point_3d.1, point_3d.2);
}
Example 3: Key-value pairs
Simple key-value pairs can be represented with tuples:
fn main() {
let records = [
("Alice", "Engineering", 3),
("Bob", "Marketing", 5),
("Charlie", "Sales", 2),
];
for (name, department, years) in records {
println!("{} works in {} for {} years", name, department, years);
}
}
Example 4: Using tuples with vectors
Tuples can be stored in collections like vectors:
fn main() {
// Vector of tuples
let mut employee_data = Vec::new();
// Add some data
employee_data.push(("Alice", 30, 75000.0));
employee_data.push(("Bob", 25, 65000.0));
employee_data.push(("Charlie", 35, 80000.0));
// Process the data
let mut total_salary = 0.0;
let mut total_age = 0;
for (name, age, salary) in &employee_data {
println!("{} is {} years old and earns ${:.2}", name, age, salary);
total_salary += salary;
total_age += age;
}
let avg_salary = total_salary / employee_data.len() as f64;
let avg_age = total_age as f64 / employee_data.len() as f64;
println!("Average salary: ${:.2}", avg_salary);
println!("Average age: {:.1}", avg_age);
}
Example 5: Partial destructuring
You can partially destructure tuples when you're only interested in certain elements:
fn main() {
let user = ("Alice", "[email protected]", "password123", 30);
// Destructure only the first and third elements
// Use _ to ignore elements we don't need
let (name, _, password, _) = user;
println!("Name: {}, Password: {}", name, password);
}
Example 6: Nested tuples
Tuples can be nested to create more complex structures:
fn main() {
// Nested tuple representing a person with an address
let person = (
"Alice",
30,
(
"123 Main St",
"Anytown",
"CA",
"12345"
)
);
// Access elements in nested tuples
let street = person.3.0;
let city = person.3.1;
let state = person.3.2;
println!("{} lives in {}, {}", person.0, city, state);
// Destructuring nested tuples
let (name, age, address) = person;
let (street_address, city_name, state_code, zip) = address;
println!("{} is {} years old and lives at {}", name, age, street_address);
}
Limitations of Tuples
- Fixed size: Once declared, you cannot add or remove elements.
- Limited indexing: You can only access elements using dot notation (.0, .1, etc.).
- No named fields: Unlike structs, tuple elements don't have names (only positions).
- Practical limit on size: While technically you can have many elements in a tuple, it becomes unwieldy with too many elements. The standard library only implements traits for tuples up to 12 elements.
When to Use Tuples
Tuples are best used when:
- You need to return multiple values from a function
- You need to group a small number of related values
- The grouping is simple and doesn't warrant creating a custom struct
- The data structure is temporary or internal to a function
For more complex data structures with named fields or when you need more flexibility, Rust's structs are generally a better choice.