Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!

:timing
:sccache 1

Tips & Traps

  1. Summary of Collections in Rust has a good summary on when to each which collection in Rust.

  2. The length of an array is considered part of its type and thus has to be defined at compile time. You cannot define a dynamic sized array. Vec is essentially dynamic sized array.

  3. An array is copyable if its element type is copyable. This has a pitfall of implicity copy if you are not careful! For example, if you iterate an array using into_iter and then use the array again, a copy of the array will be made for into_iter which is likely NOT what you want.

     :::rust
     let arr = [1, 2, 3];
     for v in arr.into_iter() {
         println!("{}", v);
     }
     for v in arr.into_iter() {
         println!("{}", v);
     }

    You have to be very careful about arrays when you work with them.

  4. Arrays of sizes from 0 to 32 (inclusive) implement the Default trait if the element type allows it. As a stopgap, trait implementations are statically generated up to size 32. This will be improved once const generics is leveraged to implement arrays.

  5. The function from_fn provides an easy way to construct multi-elements arrays. The Rust crate array-macro provides a macro array! to easily construct multi-elements arrays.

Array vs Vec in Rust

  1. Array is fixed size and allocated on a stack while Vec has a dynamic size and is allocated on the heap. Array is slightly faster than Vec (since stack is faster than heap) especially when the size of the sequence is small. Multi-dimensional array is even faster than Vec of Vec due to improved caching.

  2. An array can’t be empty at any time. When you crate an array, you immediately have to initialize it. In practice, you might have to initialize it twice, once with a zero/default value, and second time with some computed one.

  3. A Vec is not copyable even if its element type is copyable, so you won’t worry about copying a Vec accidentally. However, an array is copyable if its element type is copyable! This means that array has a pitfall of implicity copy if you are not careful! For example, if you iterate an array using into_iter and then use the array again, a copy of the array will be made for into_iter which is likely NOT what you want.

     :::rust
     let arr = [1, 2, 3];
     for v in arr.into_iter() {
         println!("{}", v);
     }
     for v in arr.into_iter() {
         println!("{}", v);
     }

    You have to be very careful about arrays when you work with them.

:sccache 1
sccache: true
use std::vec::Vec;
let arr = [1, 2, 3];
for v in arr {
    println!("{}", v);
}
for v in arr {
    println!("{}", v);
}
1
2
3
1
2
3
()
fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>())
}

Construct Arrays

struct Foo;

let arr: [Option<Foo>; 100] = Default::default();
let arr: [Option<Foo>; 100] = Default::default();
                              ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[Option<Foo>; 100]`
the trait bound `[Option<Foo>; 100]: Default` is not satisfied
help: the following other types implement trait `Default`:
  &[T]
  &mut [T]
  [T; 0]
  [T; 10]
  [T; 11]
  [T; 12]
  [T; 13]
  [T; 14]
and 27 others

(Integer) Array of Zeros

let arr: [i32; 5] = [0; 5];
arr
[0, 0, 0, 0, 0]
let arr2 = &[0; 5];
arr2
[0, 0, 0, 0, 0]
print_type_of(arr2)
[i32; 5]
()

Construct More Complicated Arrays

use array_macro::array;
let arr: [usize; 5] = std::array::from_fn(|i| i);
arr
[0, 1, 2, 3, 4]
static arr2: [usize; 5] = std::array::from_fn(|i| i);
arr2
[E0015] Error: cannot call non-const fn `std::array::from_fn::<usize, 5, [closure@src/lib.rs:2:47: 2:50]>` in statics
   [command_7:1:1]
   
 1 │ static arr2: [usize; 5] = std::array::from_fn(|i| i);
   ·                             
   ·                                         error: cannot call non-const fn `std::array::from_fn::<usize, 5, [closure@src/lib.rs:2:47: 2:50]>` in statics
───╯
let arr: [Vec<usize>; 2] = {
    let v = vec![
        vec![1, 2, 3],
        vec![4, 5, 6],
    ];
    std::array::from_fn(|i| v[i].clone())
};
arr
[[1, 2, 3], [4, 5, 6]]
let v = ["apples", "cake", "coffee"];

for text in v {
    println!("I like {}.", text);
}
I like apples.
I like cake.
I like coffee.
()
let v = ["apples".to_string(), "cake".to_string(), "coffee".to_string()];

for text in &v {
    println!("I like {}.", text);
}
I like apples.
I like cake.
I like coffee.
()

The size of the array must be determined at compile time.

let n = 5;
n
5
let arr2: [i32; n] = [0; n];
arr
let arr2: [i32; n] = [0; n];
                ^ non-constant value
attempt to use a non-constant value in a constant
help: consider using `const` instead of `let`

const n
let arr2: [i32; n] = [0; n];
                         ^ non-constant value
attempt to use a non-constant value in a constant
help: consider using `const` instead of `let`

const n
let numbers = [0, 1, 2, 3];
numbers
[0, 1, 2, 3]
numbers[0]
0
numbers[3]
3
for i in 1..3 {
    println!("{}", numbers[i])
}
1
2
()

capacity

An array does not have the method capacity since it is of fixed length.

numbers.capacity()
numbers.capacity()
        ^^^^^^^^ method not found in `[i32; 4]`
no method named `capacity` found for array `[i32; 4]` in the current scope

len

numbers.len()
4

push

numbers.push(4)
numbers.push(4)
        ^^^^ method not found in `[i32; 4]`
no method named `push` found for array `[i32; 4]` in the current scope

Size (in Bytes) of Array

std::mem::size_of_val(&numbers)
16

Iterate Through an Array

for loop / into_iter

let array: [i32; 3] = [0; 3];

for x in array { 
    println!("{}", x);
}
0
0
0
()
let words = vec!["alpha", "beta", "gamma"];
let merged: String = words.into_iter().collect();
println!("{}", merged);
alphabetagamma
let words = ["alpha", "beta", "gamma"];
let merged: String = words.into_iter().collect();
println!("{}", merged);
let merged: String = words.into_iter().collect();
                                       ^^^^^^^ value of type `String` cannot be built from `std::iter::Iterator<Item=&&str>`
a value of type `String` cannot be built from an iterator over elements of type `&&str`
help: the trait `FromIterator<&&str>` is not implemented for `String`
array.into_iter().map(|&i| array[i as usize]).sum::<i32>()
0
array.iter().map(|&i| array[i as usize]).sum::<i32>()
0

Using Array Index

for i in 0..5 {
    println!("{}", numbers[i]);
}
0
1
2
3
4
()

Using the .iter() Method

Technically speaking, an array is coerced to a slice (implicitly) before calling .iter().

for n in numbers.iter() {
    println!("{}", n);
}
0
1
2
3
4
()

Using the .iter_mut Method

This is similar to the .iter() method but allows you to update the array while iterate through it.

let mut numbers: [i32; 5] = [0, 1, 2, 3, 4];
for n in numbers.iter_mut() {
    *n *= 2;
}
println!("{:?}", numbers);
[0, 2, 4, 6, 8]
()

Iterator

map

collect

group_by

2-D Array

let mut state = [[0u8; 4]; 6];
state[0][1] = 42;
state
[[0, 42, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

Array vs Slice

An array is a collection of objects of the same type T, stored in contiguous memory. Arrays are created using brackets [], and their length, which is known at compile time, is part of their type signature [T; length].

Slices are similar to arrays, but their length is not known at compile time. Instead, a slice is a two-word object, the first word is a pointer to the data, and the second word is the length of the slice. The word size is the same as usize, determined by the processor architecture eg 64 bits on an x86-64. Slices can be used to borrow a section of an array, and have the type signature &[T].

Arrays and Slices

Arrays and Slices in Rust