Collecting Stuff together in Rust with Arrays.

Every standard, modern programming language requires a structure of some sort to follow the computer programming concept of handling and manipulating collections of data as if it were one. Rust is not deficient in these structures either. In this article we'll be looking at two of those important structures: arrays and tuples.

Arrays are a very important primitive composite data type in Rust. For the sake of comparability and comprehension I'll be comparing arrays with their equivalent in JavaScript.

Arrays.

Arrays in rust possess similarities as well as dissimilarities with JavaScript's arrays. Let's take a look at some of the similarities first.

In Rust, an array can be created like this:

let friends = ["Cosmos", "Tiwa", "Ayo"];

in JavaScript, an array takes the same shape as it's rust counterpart.

let enemies = ["Anita", "Jatto", "Dinma"];

Just like JavaScript Arrays, Rust array elements can be accessed with zero based indices.

enemies[0]; // returns "Anita"
friends[1]; //returns "Tiwa"

This is where the similarities between JavaScript and Rust come to halt.

Where the divide begins.

If you're familiar with JavaScript, you'll be aware that the length of JavaScript arrays can be increased or decreased by inserting new elements into the array whether at the starting index position, the last index position or position between using methods like splice, unshift and shift, push and pop.

Rust does things differently. The length of Rust arrays cannot be increased or reduced because rust arrays take a strict length and stick to it. Another property that differs Rust's arrays from JavaScript arrays is that they don't take elements of diverse data types unlike their JavaScript counterpart. Rust's arrays are homogenous in nature compared to the heterogeneousness of JavaScript's arrays. So, Rust's arrays can't be written like this:

let randoms = ["Sly", true, 4, 'c'];

doing this will give us an error like this:

 --> arrays.rs:20:27
   |
20 |     let randoms = ["Sly", true, 4, 'c'];
   |                           ^^^^ expected `&str`, found `bool`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

Rust's arrays have to have a single data type. So, something like:

let grades = ['A', 'B', 'C', 'D', 'E', 'F'];

Along with this, rust cannot have sparse arrays like JavaScript because it doesn't have types like null or undefined.

in JavaScript the length of an array is determined by the length property. We'll the case is a little different for Rust as it uses the len() method for that same purpose.

Annotations.

if we ran the following lines of code:

fn main() {
    let tubbies = ["Tinkey-winkey", "Dipsey", "Laa-laa", "Po"];
    println!("Teletubbies: {}", tubbies);
}

the rust compiler spits out this error:

error[E0277]: `[&str; 4]` doesn't implement `std::fmt::Display`
  --> arrays.rs:20:30
   |
20 |     println!("Teletubbies: {}", tubbies);
   |                                 ^^^^^^^ `[&str; 4]` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `[&str; 4]`
   = 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` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

It seems like this code is correct, but the compiler tells us otherwise and show us what's wrong in our code. Firstly, just like other primitive types, arrays have to be type annotated. Secondly, println! is currently set to use something called a Display trait (more on this in a later post), and the compiler suggests that we switch the trait to another trait called a Debug trait that will allow us print out our array without any hitches. Let's fix these errors.

fn main(){
    let tubbies: [&str; 4] = ["Tinkey-winkey", "Dipsey", "Laa-laa", "Po"];

    println!("Teletubbies: {:?}", tubbies);
}

Now when we run our code, the compiler no longer tells us there's any error. Let's breakdown what we did to fix the error.

  • [&str; 4] is how arrays are annotated. The first character before the semicolon points to the data type of the elements in the array and the number after the semicolon represents the number of elements in the array, that cannot be increased or reduced.

  • the :? operator tells println!() that we want to switch from using the Display trait to using the Debug trait so we can print out the value of our array.

What else can be done with an array?

Arrays in Rust can also be initialized but they have to be made mutable first. Recall the mut keyword?

let mut grades: [0, 5]; //initialize the array
grades[0]: 'A';
grades[1]: 'B';
grades[2]: 'C';
grades[3]: 'D';
grades[4]: 'E';

in the [0, 5], 0 represents the initial value at the initialized state of the array before values start to get added to the array. 5 defines the length of the static length of the array.

Destructuring arrays in rust.

Rust arrays can be destructured.

let [my_grades, _, _] = grades;

_ represents a throw away value that is not assigned and cannot be used. We could also take this approach:

let [my_grades, ..] = grades;

if you assumed that .. is the rest operator in Rust, then you're correct.

Arrays in Rust are the beginning of the expression of its static typing capabilities. In the next post we'll look into the other primitive composite structure in rust: Tuples.