What a tuple is
Some values belong together: a movie has a title and a year, a point has an x and a y. A tuple keeps a few values like these in one variable. The values can have different types, and the number of values is part of the tuple's type.
let movie = ("Frozen", 2013);
println!("{}", movie.0);
println!("{}", movie.1);
println!("{:?}", movie);
Frozen
2013
("Frozen", 2013)
The type of movie is (&str, i32). You read a value with a dot and its position, .0 for the first and .1 for the second. Square brackets don't work on a tuple, because the positions are part of the type, not numbers computed at run time. The {:?} format prints the whole tuple at once, the way it prints a vector.
Unpacking
A let with a pattern on the left takes a tuple apart into separate variables:
let (title, year) = movie;
println!("{} {}", title, year);
Frozen 2013
The names go in the same order as the values. This is the same pattern a loop uses in for (name, points) in &scores: each item is a tuple, and the loop unpacks it for you.
Returning two values
Rust functions return one value, so a function that needs to give back two returns a tuple. The return type is the tuple's type, for example (i32, i32), and return (a, b); builds the tuple. The caller unpacks the result with the same let (a, b) = ... pattern.
Changing a tuple
As with any Rust value, a tuple made with plain let can't be changed. Assigning to one of its fields is a compile error:
error[E0594]: cannot assign to `movie.1`, as `movie` is not declared as mutable
With let mut movie, the assignment movie.1 = 2014; works. Even then, a tuple never gets more values, and each position keeps its type. If you need a collection that grows, use a vector.
The exercise, step by step
The task asks for three things, and the code already prints each result.
- Return the tuple from
split_time. The whole hours areminutes / 60and the minutes left areminutes % 60. Integer division drops the fraction, so 135 gives 2 hours and 15 minutes. - Unpack
petintonameandage, in the order the tuple holds them. - Call
split_time(135)and unpack its result intohoursandminsthe same way.
fn split_time(minutes: i32) -> (i32, i32) {
return (minutes / 60, minutes % 60);
}
fn main_function() {
let pet = ("Rex", 4);
println!("{}", pet.0);
let (name, age) = pet;
println!("{} is {}", name, age);
let (hours, mins) = split_time(135);
println!("{} walked {} h {} min", name, hours, mins);
}
Run, it prints:
Rex
Rex is 4
Rex walked 2 h 15 min
Common mistakes
- Square brackets.
movie[0]is an error. Usemovie.0. - Wrong number of names.
let (a, b, c) = movie;doesn't compile, because the pattern must have as many parts as the tuple has values. - Wrong order. The names follow the order of the values, so
let (year, title) = movie;would put the title inyear. - Forgetting the type of a returned tuple. A function that returns two numbers needs
-> (i32, i32)in its signature, not just-> i32.
Tuples are used throughout this chapter: a vector can hold tuples, a sort can compare their fields, and loops can unpack them as they go.
Key takeaways
- A tuple groups a fixed number of values, possibly of different types, in one variable.
- Read a value with
.0,.1and so on, or unpack all of them withlet (a, b) = tuple;. - A function returns several values by returning a tuple, and its return type is the tuple's type.
- A tuple needs
let mutto change a field, and it never changes size or types.
