Arrays and Tuples - Hello, Rust!


Table of contents

Introduction

There are two primitive compound types in Rust: Arrays and Tuples

Arrays

You've probably heard of Arrays before. Arrays in Rust are similar and dissimilar to Arrays in JavaScript in different ways. Let's talk about the similarities first.

let spidermen = ["Tobey Maguire", "Andrew Garfield", "Tom Holland"];

The statement above is both valid JavaScript and Rust.

Like JavaScript, you can access the elements on the array spidermen by using zero based indices, for example Andrew Garfield is spidermen[1].

This is pretty much where the similarities end.

In Rust, all elements in an Array must be of the same type. The above example works because all the elements in spidermen are String slices (more on these soon) but if you tried to create:

let spidermen = ["Tobey Maguire", "Andrew Garfield", "Tom Holland", 4];

It would fail with:

error[E0308]: mismatched types
--> hello-rust.rs:2:73
|
2 |     let spidermen = ["Tobey Maguire", "Andrew Garfield", "Tom Holland", 4];
|                                                                         ^ expected `&str`, found integer


error: aborting due to previous error

Arrays in Rust also have a strictly defined length and since there is no undefined or null in Rust, arrays cannot be sparse.

This means that you can't push another element to the above array when they inevitably cast a new Spider-Man in a few years. To have a list of items that can grow or shrink in size, you need a Vec! We'll learn about these later.

Given these limitations, Arrays in Rust are useful for lists with known sizes and elements of an homogenous type, like a list of top three scores at your local bowling alley.

let top_scores: [u32; 3] = [292, 170, 140];

Notice the semicolon separator in the type annotation: u32 is the type of each element of which there are 3.

You can even initialise an Array with a length and a default value for each element but as we've learnt previously, it needs to be mutable (declared with mut) if you want to reassign values!

let mut top_scores = [0; 3];
top_scores[0] = 292;
top_scores[1] = 170;
top_scores[2] = 140;

In the above example, 0 is the default value of each element and there are 3 of them.

Unlike JavaScript, the length of an Array in Rust is not a property but a method on the Array called len.

println!("{}", top_scores.len());

Finally, let's try to print the entire Array with println!.

println!("{}", top_scores);

This will throw an error during compilation that is along the lines of:

error[E0277]: `[{integer}; 3]` doesn't implement `std::fmt::Display`
--> hello-rust.rs:6:20
|
6 |     println!("{}", top_scores);
|                    ^^^^^^^^^^ `[{integer}; 3]` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `[{integer}; 3]`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)


error: aborting due to previous error

We'll get into what this means in detail when we dive deep into Traits but for now, you can print the array as follows

println!("{:?}", top_scores);

The :? makes println! use the Debug "trait" instead of the default Display and lets you quickly print out values as you learn.

Tuples

Tuples are the other primitive compound type in Rust. JavaScript doesn't have Tuples so this may seem a little foreign but you'll appreciate these. Tuples are simply put a collection of multiple values of various types but with a fixed length again!

For example, here is a mutable Tuple of product name, cost and availability:

let mut product = ("iPhone 12 Pro Max", 1099, true);

We can set the tuple to:

product = ("PS5", 499, false);

But not to:

product = ("PS5", 499.99, false);

Because product was initialised as a (&str, i32, bool) tuple and we tried to set the middle value to a floating point value.

Tuples can have multiple types in them but the type of a position cannot change after initialisation.

👉

There is a special Tuple in Rust called the unit tuple. () represents the unit tuple. It has no values in it. This is what is returned by functions that return "nothing" in Rust.

Both Arrays and Tuples can be destructured in Rust.

Here are the destructured values for the top score and the general availability of a PS5 from the above examples:

let [my_score, _, _] = top_scores;
let (_, _, is_available) = product;
👉

_ represents a throwaway value that is not assigned and therefore cannot be used.

If you didn't know the number of elements in top_scores and just wanted to get the first one, you could even do this:

let [my_score, ..] = top_scores;
👉

Funnily though, the "rest" operator in Rust is .. and not ....