Nime

Rust: tuple vs array

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];

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");

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];

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");

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

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

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);

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

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

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.

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

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
}