Rust Tutorial – Part 5 – Tuples and more

Tuples are perhaps the most fun data type we’ll encounter in Rust. They are convenience and elegance rolled into one, as we’ll soon see. The idea behind tuples is to let you group together items of different types. If you’re coming from a language like PHP, you might like to think of associative arrays; the Python equivalent would be the tuple or the list; the C equivalent would be structs; and so on.

Rust tuples are convenient

Here’s how you define a tuple in Rust:

let tup = (500, 6.4, 1);

As you can see, Rust recognizes a tuple from the round brackets. You’ll note that we have two (or three?) different data types here: two integers and one float, grouped into one.

So, how do you access these items? Rust provides a quite neat syntax, using the dot operator and using the index number of the items (remember, indices in computer science begin with zero):

fn main() {
    let tup = (500, 6.4, 1);
    println!("{}, {}", tup.0, tup.1);
}

No prizes for guessing; this prints 500, 6.4.

Rust tuples are elegant

And now, ladies and gentlemen, comes the part we’ve been hyping up so much: elegance. Some will argue that this is more convenience than elegance, but that’s a subjective thing.

You can also access a tuple’s items using something known as pattern matching:

fn main() {
    let tup = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("{}, {}, {}", x, y, z);
}

Whoa! Let’s step back and admire this masterpiece for a minute. The first line, tup = (500, 6.4, 1); creates the tuple. We’re then “unrolling” or “unpacking” the tuple directly into three variables called x, y and z. In one single stroke, x becomes 500, y becomes 6.4, and z becomes 1, as shown by the output:

500, 6.4, 1

The important thing here is that the types of the variables must also match. Let’s try to be smart and see how it goes:

fn main() {
    let tup = (500, 6.4, 1);
    let (x, y, z): (bool, f32, i16) = tup;
    println!("{}, {}, {}", x, y, z);
}

We’ve defined three mutable variables (because they’ll need to change as soon as they receive values from the decomposed tuple). Of these, x is the unfortunate one: it’s a boolean, but it’s going to be slapped in the face with 500. Rust detects our “cleverness” and cries foul:

error[E0308]: mismatched types
 --> 01_variables_and_mutation.rs:6:17
  |
6 |     (x, y, z) = tup;
  |                 ^^^ expected bool, found integral variable
  |
  = note: expected type `(bool, f32, i16)`
             found type `({integer}, {float}, {integer})`

Look closely: Rust says it expected the order of the receiving types to be (bool, f32, i16) but found ({integer}, {float}, {integer}) in the tuple.

This is just a preview of pattern matching. The concept goes much deeper, and although simple to understand, results in very clean code that is easy to understand. We’ll see more of pattern matching later on in this tutorial.

Other data types

Finally, a quick word on other data types; Rust also has arrays for grouping items of similar type, but there’s one striking difference: arrays have a fixed length! Yes, that alone is enough to make eyes pop out, but Rust has a reason for doing so. If you really need array-like constructs that can grow in size, Rust offers vectors.

But enough for now. We’ll dive into these types when we need to.

[The link to the next tutorial is here.]

Leave a Reply

Your email address will not be published. Required fields are marked *