Introduction to Rust Programming πŸ¦€

Rust Basics πŸ¦€

Rust Basics Series #4: Arrays and Tuples in Rust

In the fourth chapter of the Rust series, learn about compound data types, Arrays and Tuples.

In the previous post, you learned about the Scalar data types in Rust. They are integers, floating points, characters and booleans.

In this article, we shall look at the Compound data types in the Rust programming language.

What is compound data type in Rust?

Compound data types consist can store multiple values in a variable. These values may either be of the same scalar data type, or maybe of different scalar types.

The Rust programming language has two such data types:

  • Arrays: Stores multiple values of the same type.
  • Tuples: Stores multiple values, either of the same type or even of different types.

So let's look at them!

Arrays in Rust

Arrays in the Rust programming language have the following properties:

  • Every element must have the same type
  • Arrays have a fixed length
  • Arrays are stored in the stack i.e., data stored in it can be accessed swiftly

The syntax to create an array is as follows:

// without type annotation
let variable_name = [element1, element2, ..., elementn];

// with type annotation
let variable_name: [data_type; array_length] = [element1, element2, ..., elementn];

The elements of an array are declared inside square brackets. To access an element of an array, the index to be accessed is specified inside square brackets.

Let's look at an example program to understand this better.

fn main() {
    // without type annotation
    let greeting = ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'];

    // with type annotation
    let pi: [i32; 10] = [1, 4, 1, 5, 9, 2, 6, 5, 3, 5];

    for character in greeting {
        print!("{}", character);
    }

    println!("\nPi: 3.1{}{}{}{}", pi[0], pi[1], pi[2], pi[3]);
}

Here, I define one character array and another array that stores i32 types in it. The greeting array has the characters of the string "Hello world!" stored in it as individual characters. The array pi has the first 10 values of Pi after the decimal values stored in it as individual numbers.

I then print every character of the greeting array using the for loop. (I will get into loops very soon.) Then, I print the first 4 values of the pi array.

Hello world!
Pi: 3.11415

If you wish to create an array where every element is y and occours x number of times, you can do this in Rust with the following shortcut:

let variable_name = [y; x];

Let's look at a demonstration...

fn main() {
    let a = [10; 5];

    for i in a {
        print!("{i} ");
    }
    println!("");
}

I create a variable a which will be of length 5. Each element in that array will be '10'. I verify this by printing every element of the array using the for loop.

It has the following output:

10 10 10 10 10
🀸
As an exercise, try creating an array of length x and access the x+1st element of the array. See what happens.

Tuples in Rust

A Tuple in the Rust programming language has the following properties:

  • Tuples, like Arrays have a fixed length
  • Elements can be of same/different Scalar data types
  • The Tuple is stored on the stack i.e. faster access

The syntax to create a tuple is as following:

// without type annotation
let variable_name = (element1, element2, ..., element3);

// with type annotation
let variable_name: (data_type, ..., data_type) = (element1, element2, ..., element3);

The elements of a tuple are written inside the round brackets. To access an element, the dot operator is used and is followed by the index of said element.

fn main() {
    let a = (38, 923.329, true);
    let b: (char, i32, f64, bool) = ('r', 43, 3.14, false);

    println!("a.0: {}, a.1: {}, a.2: {}", a.0, a.1, a.2);
    println!("b.0: {}, b.1: {}, b.2: {}, b.3: {}", b.0, b.1, b.2, b.3);

    // destructuring a tuple
    let pixel = (50, 0, 200);
    let (red, green, blue) = pixel;
    println!("red: {}, green: {}, blue: {}", red, green, blue);
}

In the above code, on line 2 and 3 I declare two tuples. These just contain random values that I made up on the spot. But look closely, the data type of each element in both tuples is different. Then, on line 5 and 6, I print each element of both tuples.

On line 9, I declare a tuple called pixel which has 3 elements. Each element is the magnitude of colors red, green and blue to make up a pixel. This ranges from 0 to 255. So, ideally, I would annotate the type to be (u8, u8, u8) but that optimization is not required when learning ;)

Then, on line 10, I "de-structure" each value of the pixel tuple and store it in individual variables red, green and blue. Then, instead of printing the values of the pixel tuple, I print the values of the red, green and blue variables.

Let's see the output...

a.0: 38, a.1: 923.329, a.2: true
b.0: r, b.1: 43, b.2: 3.14, b.3: false
red: 50, green: 0, blue: 200

Looks good to me :)

Bonus: Slices

Strictly speaking, slices aren't a type of compound data type in Rust. Rather, a slice is... a slice of an existing compound data type.

A slice consists of three elements:

  1. A starting index
  2. The slice operator (.. or ..=)
  3. An ending index

Following is an example of using a slice of an Array.

fn main() {
    let my_array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let my_slice = &my_array[0..4];

    for element in my_slice {
        println!("{element}");
    }
}

Like C and C++, the ampersand is used to store the reference (instead of a raw pointer) of a variable. So &my_array means a reference to the variable my_array.

Now, coming to the slice. The slice is denoted by the [0..4]. Here, 0 is the index of where to start the slice. And 4 is where the slice ends. The 4 here is a non-inclusive index.

Following is the program output to better understand what is happening:

0
1
2
3

If you want an inclusive range, you can instead use ..= as the slice operator for an inclusive range.

fn main() {
    let my_array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let my_slice = &my_array[0..=4];

    for element in my_slice {
        println!("{element}");
    }
}

Now, this range is from the 0th element to the 4th element and below is the output to prove that:

0
1
2
3
4

Conclusion

This article about the Rust programming language covers the compound data types in some depth. You learned to declare and access values stored in the Array and Tuple types. Additionally, you looked at the Slice "type" and also how to de-structure a tuple.

In the next chapter, you'll learn about using functions in Rust programs.

Rust Basics Series #5: Functions in Rust
In this chapter of the Rust Basics series, learn to use functions and return values from them with the help of examples.

Stay tuned.