Rust Tuples
A tuple groups several values of possibly different types into one. It’s a quick way to bundle related things together without defining a whole new type for them.
Creating a tuple
Section titled “Creating a tuple”fn main() { let person: (&str, i32, bool) = ("Alice", 30, true); println!("{:?}", person);}Output:
("Alice", 30, true)Notice the tuple mixes a &str, an i32, and a bool in one value — something an array can’t do, since every element in an array must be the same type.
To print a whole tuple, use {:?} (debug formatting) instead of {}. Plain {} doesn’t work for tuples.
Accessing elements by position
Section titled “Accessing elements by position”Use .0, .1, .2, and so on — tuples are indexed starting at zero:
fn main() { let person = ("Alice", 30, true);
println!("Name: {}", person.0); println!("Age: {}", person.1); println!("Active: {}", person.2);}Output:
Name: AliceAge: 30Active: trueThe position must be a plain number written in the code. You can’t use a variable, like person.i, because each position can hold a different type, and Rust has to know the type at compile time.
Destructuring a tuple
Section titled “Destructuring a tuple”You can pull every value out into its own variable in one line:
fn main() { let person = ("Alice", 30, true); let (name, age, is_active) = person;
println!("{name} is {age}, active: {is_active}");}Output:
Alice is 30, active: trueThis is often clearer than repeated .0, .1, .2 access, especially once a tuple has more than two or three fields.
Returning multiple values from a function
Section titled “Returning multiple values from a function”Tuples are the natural way to return more than one value, since a function can only declare one return type:
fn min_max(numbers: &[i32]) -> (i32, i32) { let min = *numbers.iter().min().unwrap(); let max = *numbers.iter().max().unwrap(); (min, max)}
fn main() { let (smallest, largest) = min_max(&[4, 8, 1, 9, 3]); println!("min: {smallest}, max: {largest}");}Output:
min: 1, max: 9Don’t worry about &[i32], .iter() or .unwrap() yet. They’re covered on the Slices, Iterators and Option pages. The part to notice here is the return type (i32, i32) and how main destructures it into two variables right away.
Changing tuples and swapping values
Section titled “Changing tuples and swapping values”If a tuple is declared with mut, you can change its fields one at a time, using the same .0, .1 positions. The values can change, but the shape can’t: a tuple always keeps the number and types of fields it started with.
Destructuring also works the other way: you can assign into existing variables. That gives you a neat one-line swap, with no temporary variable. If you only want some of the fields, write _ for the ones you don’t need:
fn main() { let mut point = (3, 4); point.0 += 10; // change one field in place println!("point is {:?}", point);
let mut a = 1; let mut b = 2; (a, b) = (b, a); // swap the two values println!("a = {a}, b = {b}");
let (x, _) = point; // keep x, ignore the second field println!("x = {x}");}Output:
point is (13, 4)a = 2, b = 1x = 13The swap works because Rust builds the right side, (b, a), first. Only then does it unpack it into a and b. There’s much more you can do with _ and patterns on the Pattern Matching page.
The empty tuple: ()
Section titled “The empty tuple: ()”You’ve actually already met a tuple without realizing it. () is called the unit type, and it’s what a function returns when it doesn’t return anything meaningful — it’s the implicit return type of fn main().
When to use a tuple vs a struct
Section titled “When to use a tuple vs a struct”Tuples are great for quick, unnamed groupings — especially returning two or three values from a function. Once a group of values represents a real concept with named fields (like a Person with a name and age you’ll refer to by name throughout your code), a struct is usually the better, more readable choice.
Practice
Section titled “Practice”1. Sum and product
Section titled “1. Sum and product”Write a function sum_and_product(a: i32, b: i32) -> (i32, i32) that returns both the sum and the product of two numbers. In main, call it with 5 and 6 and destructure the result. Hint: the last line of the function is the tuple itself, (a + b, a * b), with no semicolon.
Show solution
fn sum_and_product(a: i32, b: i32) -> (i32, i32) { (a + b, a * b)}
fn main() { let (sum, product) = sum_and_product(5, 6); println!("sum = {sum}, product = {product}");}Output:
sum = 11, product = 302. A rectangle made of two corners
Section titled “2. A rectangle made of two corners”A rectangle can be stored as a tuple of two points, where each point is itself a tuple: let rect = ((1, 2), (5, 5));. Work out its width, height and area. Hint: tuples can be nested, and rect.1.0 means “the first field of the second point”.
Show solution
fn main() { let rect = ((1, 2), (5, 5));
let width = rect.1.0 - rect.0.0; let height = rect.1.1 - rect.0.1;
println!("width {width}, height {height}, area {}", width * height);}Output:
width 4, height 3, area 12You can also destructure both points at once: let ((x1, y1), (x2, y2)) = rect;. That’s longer to type, but x2 - x1 is easier to read than rect.1.0 - rect.0.0. When the dots start to pile up like this, it’s a sign a struct with named fields would be clearer.
- A tuple groups values of possibly different types:
(name, age, is_active). - Access fields with
.0,.1,.2, or pull them all out at once with destructuring. - Tuples are a natural way to return multiple values from a function.
- A
muttuple’s fields can change, but its size and types can’t.(a, b) = (b, a);swaps two values. - For named, reusable groupings, reach for a struct instead.
Quick check
Section titled “Quick check”Quick check
1. What can a tuple hold that an array cannot?
2. How do you access the second element of a tuple called person?
3. After let mut a = 1; let mut b = 2; (a, b) = (b, a);, what are a and b?
Score: 0 / 3
Read more
Section titled “Read more”- The Tuple Type — The Rust Book: the official introduction to creating, destructuring and indexing tuples.
- Tuples — Rust by Example: runnable examples with nested tuples and tuples as function arguments.
- The
tupletype — standard library docs: which traits tuples support, like comparing, copying and printing with{:?}. - The
unittype — standard library docs: what()is and where it shows up. - Destructuring assignments — The Rust Reference: the exact rules behind
(a, b) = (b, a);.