Arrays

Dear Computer

Chapter 10: Everything Revisited

Arrays

Arrays in Rust have a fixed size and element type. Additionally, each element must be given a value at initialization. The values may be enumerated in a list literal, as in this declaration:

Rust
let xs = [1, 2, 4, 8, 16, 32, 64, 128, 256];

The inferred type of xs is [u32; 9], which means an array of 9 u32 values. We could have written the type explicitly:

Rust
let xs: [u32; 9] = [1, 2, 4, 8, 16, 32, 64, 128, 256];

The size is part of the array's type. An array with a different size has a different type. If we add another element to the source code, we must also increase the size in the type from 9 to 10. Otherwise the code won't compile.

Our other option is to initialize all elements of the array with a default value using a repeat expression of the form [value; count]. This declaration creates an array of 9 100s:

Rust
let xs = [100; 9];

We access elements of the array with the subscript operator:

Rust
println!("{}", xs[0]);

Out-of-bounds indices cause the program to crash, which is better than accessing unsafe memory. If there's a chance that we may have an out-of-bounds index, we should either check the bounds manually or use the get method, which returns an Option.

Though an array must be given initial values, we may overwrite these values if the array is mutable. This code creates an array of zeroes but then overwrites them with powers-of-two:

Rust
let mut xs = [0; 9];
for i in 0..xs.len() {
  xs[i] = 1 << i;
}

In the languages you've learned previously, you have passed arrays around to other functions with little concern for performance. Arrays are generally passed as pointers or references, which makes them as cheap to pass as primitive types. However, this is not the case in Rust. This program passes an independent copy of the array to printArray:

Rust
fn main() {
  let xs = [0; 1000];
  printArray(xs);
}

fn printArray(items: [u32; 1000]) {
  println("{:?}", items);
}

Additionally, the printArray function is not very flexible. It only accepts arrays of size 1000. Both the expensive copy and the size inflexibility are fixed by passing an array slice instead of the full array. A slice is passed when we apply the & operator to the array:

Rust
fn main() {
  let xs = [0; 1000];
  printArray(&xs);              // pass as reference
}

fn printArray(items: &[u32]) {  // no size is declared
  println("{:?}", items);
}

An array slice is to an array as &str is to String. As with &str, an array slice is a reference to a fixed-size window of contiguous elements. The array behind the slice supports non-destructive operations like subscripting and searching and operations that generate new structures, like join.

← EnumsVectors →