DEV Community

Nicky Meuleman
Nicky Meuleman

Posted on • Originally published at nickymeuleman.netlify.app

Rust: tuple vs array

Tuples group values with a variety of types.

Arrays group values of a single type.

They both have a fixed size.

While arrays are less flexible in content, they are more flexible in usage.


The array and the tuple are both compound types, that means they can hold multiple values.
An array and a tuple are both groups of other values.

Tuples are enclosed by parentheses ().
Every element in it is separated by a comma ,.

Arrays are enclosed by square braces [].
Every element in it is separated by a comma ,.

They are both fixed in size, they can't shrink or grow.
A vector is a similar collection type that is allowed to change in size.

So far, they sound very similar, with just some arbitrary syntax differentiating them.

Types

For an array, every item in that group has to have the same type.
If a value in that array is of type f64, every item in that array must be of type f64.

let arr = [1.0, 2.5, 3.14];
Enter fullscreen mode Exit fullscreen mode

For tuples, items in that group can have different types.
All values in a tuple can be the same type, but they can be different too.

let tup = (1, 2.5, "yummy pie");
Enter fullscreen mode Exit fullscreen mode

Annotating the types

The Rust compiler is smart and will try to figure out which types it's dealing with, that means explicit type declarations are not always needed.

For arrays, the type declaration consists of enclosing square brackets. Inside is the type of the elements followed by a semicolon ;, and the length or the array.

let arr: [f64;3] = [1.0, 2.5, 3.14];
Enter fullscreen mode Exit fullscreen mode

For tuples, the type declaration consists of enclosing parentheses. Inside are the types of each element, each one separated by a comma ,.

let tup: (i32, f64, &str) = (1, 2.5, "yummy pie");
Enter fullscreen mode Exit fullscreen mode

Accessing information

Element by index

Indexes for both tuples and arrays start at 0.
Tuples, somewhat surprisingly, use the . to access an element at a specific index.

let first_arr = arr[0]; // 1.0
let first_tup = tup.0; // 1
Enter fullscreen mode Exit fullscreen mode

Element by destructuring

let (tup_first, tup_second, tup_third) = tup;
let [arr_first, arr_second, arr_third] = arr;
println!("tup_first: {}, tup_second: {}, tup_third: {}", tup_first, tup_second, tup_third);
// tup_first: 1, tup_second: 2.5, tup_third: "yummy pie"
println!("arr_first: {}, arr_second: {}, arr_third: {}", arr_first, arr_second, arr_third);
// arr_first: 1.0, arr_second: 2.5, arr_third: 3.14
Enter fullscreen mode Exit fullscreen mode

It's a lot more common to destructure tuples.
It's especially handy if a function returns 2 values in a tuple and you want to name each value individually.

fn num_and_double(num: i32) -> (i32, i32) {
    (num, num * 2)
}
let (lucky_num, double_lucky_num) = num_and_double(3);
Enter fullscreen mode Exit fullscreen mode

Length

An array has some metadata associated with it, like its length.
A tuple does not.
Tuples are essentialy structs with anonymous field names.

let arr_len = arr.len(); // 3
// let tup_len = tup.len(); // error
Enter fullscreen mode Exit fullscreen mode

Looping

Iterating over a tuple doesn't work.
For a tuple, each iteration could result in a different type, that doesn’t work.
Since arrays can only hold a single type, they don't have that limitation.

// for num in &tup {
//     println!("num: {}", num);
// }
// error

for num in &arr {
    println!("num: {}", num);
}
// num: 1.0
// num: 2.5
// num: 3.14
Enter fullscreen mode Exit fullscreen mode

Ok, fine. I'll iterate over a number range and access each item in the tuple via the dot synt...NOPE

Tuples can’t be dynamically indexed.

The compiler needs to be able to figure out what type every value is, indexing into it with a variable makes this impossible.
If the compiler doesn't know which index is being used, it can not figure out what type the value at that index is.

Here’s an (slightly contrived) example of why you can’t do stuff like that in Rust:

let myTuple = (1, 2, "false");
let x = randomNumberGenerator();
let y = myTuple.x;

In this case, the compiler wouldn’t be able to figure out at compile-time what type y should be!
Whereas with an array, you know (and the compiler knows) that indexing is always going to return the same type.

17cupsofcoffee
let index = 2;

let arr_pi = arr[2]; // 3.14
let also_arr_pi = arr[index]; // 3.14

let tup_pie = tup.2; // "yummy pie"
// let also_tup_pie = tup.index; // error
Enter fullscreen mode Exit fullscreen mode

Code summary

fn main() {
    let tup = (1,2,3);
    let arr = [1,2,3];
    let index = 2;

    println!("Array: {:?}", arr[2]); // Array: 3
    println!("Array: {:?}", arr[index]); // Array: 3
    println!("Tuple: {:?}", tup.2); // Tuple: 3
    // println!("Tuple: {:?}", tup.index); // error

    for num in &arr {
        println!("num: {}", num);
    }
    // num: 1
    // num: 2
    // num: 3

    // for num in &tup {
    //     println!("num: {}", num);
    // }
    // error

    let (tup_first, tup_second, tup_third) = tup;
    let [arr_first, arr_second, arr_third] = arr;

    println!("tup_first: {}, tup_second: {}, tup_third: {}", tup_first, tup_second, tup_third);
    // tup_first: 1, tup_second: 2, tup_third: 3

    println!("arr_first: {}, arr_second: {}, arr_third: {}", arr_first, arr_second, arr_third);
    // arr_first: 1, arr_second: 2, arr_third: 3

    // tup.len() // error
    arr.len() // 3
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)