Skip to content

Rust - Tuple

A tuple is a general-purpose, compound data type for grouping together a number of values with a variety of types into one compound type. Tuples have a fixed length: once declared, they cannot grow or shrink in size.

They are useful when you want to combine a few related pieces of information without the overhead of creating a full struct.

You create a tuple by writing a comma-separated list of values inside parentheses. Each position in the tuple has a type, and the types of the different values in the tuple don’t have to be the same.

You can access a tuple element directly by using a period (.) followed by the index of the value you want to access. The first index is 0.

fn main() {
// Creating a tuple with type annotations
let user_data: (String, u32, bool) = (String::from("Alice"), 30, true);
// Accessing elements by index
let user_name = &user_data.0;
let user_age = user_data.1;
println!("User: {}, Age: {}", user_name, user_age);
// You can use the debug formatter `{:?}` to print the whole tuple
println!("Full user data: {:?}", user_data);
}
User: Alice, Age: 30
Full user data: ("Alice", 30, true)

Destructuring is a convenient way to break a tuple up into its individual pieces. You can do this with a let pattern, which creates variables for each element of the tuple.

fn main() {
let http_status = (404, "Not Found");
// Destructuring the tuple into two variables
let (status_code, status_message) = http_status;
println!("The status code is {}", status_code);
println!("The status message is '{}'", status_message);
// Note: the original tuple `http_status` is still available if its types
// are `Copy`. If it contains non-Copy types like `String`, the values would be moved.
println!("Original tuple: {:?}", http_status);
}
The status code is 404
The status message is 'Not Found'
Original tuple: (404, "Not Found")

A common and idiomatic use of tuples is to return multiple values from a function. This is often cleaner than returning a struct if the structure is simple and only used in one place.

// This function calculates the length and first word of a sentence.
// It returns a tuple containing a usize and a String.
fn calculate_length_and_first_word(s: String) -> (usize, String) {
let length = s.len();
let first_word = s.split_whitespace().next().unwrap_or("").to_string();
(length, first_word) // Return the tuple
}
fn main() {
let sentence = String::from("Rust programming is fun");
let (len, word) = calculate_length_and_first_word(sentence);
println!("The sentence has {} bytes.", len);
println!("The first word is '{}'.", word);
// Trying to use `sentence` here would fail, because it was moved into the function.
// println!("{}", sentence); // Error: value borrowed here after move
}

Tuples and structs are similar, but have different ideal use cases:

  • Use a tuple when: You have a small, fixed-size collection of related but different-typed data, and the meaning of each element is clear from its position alone. Returning an error code and a result is a classic example.
  • Use a struct when: Your data has more complexity. Giving each field a name makes the code more readable and maintainable. If you expect to pass this data structure around through multiple functions, a struct is almost always the better choice.

There is a special tuple with no elements, written as (). This is called the unit type, and it has only one value, also written (). The unit type is implicitly returned by functions that don’t have an explicit -> return type. It signifies an empty or valueless return.

fn does_not_return_a_value() {
println!("I return the unit type implicitly!");
}
fn main() {
let result = does_not_return_a_value();
// `result` here has the value `()` and the type `()`
// This is not very useful, but demonstrates the concept.
}