Ben Chuanlong Du's Blog

It is never too late to learn.

Array in Rust

In [ ]:
: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.

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

Construct Arrays

In [10]:
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

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

Construct More Complicated Arrays

In [12]:
use array_macro::array;
In [4]:
let arr: [usize; 5] = std::array::from_fn(|i| i);
arr
Out[4]:
[0, 1, 2, 3, 4]
In [7]:
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
───╯
In [8]:
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
Out[8]:
[[1, 2, 3], [4, 5, 6]]
In [ ]:

In [6]:
let v = ["apples", "cake", "coffee"];

for text in v {
    println!("I like {}.", text);
}
I like apples.
I like cake.
I like coffee.
Out[6]:
()
In [7]:
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.
Out[7]:
()

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

In [11]:
let n = 5;
n
Out[11]:
5
In [12]:
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
In [13]:
let numbers = [0, 1, 2, 3];
numbers
Out[13]:
[0, 1, 2, 3]
In [14]:
numbers[0]
Out[14]:
0
In [15]:
numbers[3]
Out[15]:
3
In [16]:
for i in 1..3 {
    println!("{}", numbers[i])
}
1
2
Out[16]:
()

capacity

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

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

len

In [18]:
numbers.len()
Out[18]:
4

push

In [19]:
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

In [20]:
std::mem::size_of_val(&numbers)
Out[20]:
16

Iterate Through an Array

for loop / into_iter

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

for x in array { 
    println!("{}", x);
}
0
0
0
Out[21]:
()
In [3]:
let words = vec!["alpha", "beta", "gamma"];
let merged: String = words.into_iter().collect();
println!("{}", merged);
alphabetagamma
In [4]:
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`
In [25]:
array.into_iter().map(|&i| array[i as usize]).sum::<i32>()
Out[25]:
0
In [26]:
array.iter().map(|&i| array[i as usize]).sum::<i32>()
Out[26]:
0

Using Array Index

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

Using the .iter() Method

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

In [11]:
for n in numbers.iter() {
    println!("{}", n);
}
0
1
2
3
4
Out[11]:
()

Using the .iter_mut Method

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

In [2]:
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]
Out[2]:
()

Iterator

map

collect

group_by

In [ ]:

2-D Array

In [2]:
let mut state = [[0u8; 4]; 6];
state[0][1] = 42;
In [3]:
state
Out[3]:
[[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

Comments