Rust array
last modified October 25, 2026
In this article we show how to work with arrays in Rust.
An array is a fixed-size collection of elements of the same type. The elements are stored next to each other in memory and each element can be referred to by an index. The indexes are zero-based; the index of the first element is 0.
The type of an array is written as [T; N], where T is
the element type and N the number of elements. The length is part
of the type: [i32; 3] and [i32; 4] are two different
types. The size of an array must be known at compile time and it cannot grow or
shrink. When we need a collection that changes its size, we use a
vector.
The examples were tested with Rust 1.97 and the 2024 edition. They are run as
Cargo projects with cargo run -q.
Array initialization
Arrays are created with a pair of [] brackets. There are several
ways to fill them with values.
fn main() {
let vals: [i32; 5] = [1, 2, 3, 4, 5];
println!("{vals:?}");
let words = ["soup", "falcon", "water", "tree"];
println!("{words:?}");
let vals2: [i32; 10] = [3; 10];
println!("{vals2:?}");
let vals3 = [0; 5];
println!("{vals3:?}");
let squares: [u32; 6] = std::array::from_fn(|i| (i as u32 + 1).pow(2));
println!("{squares:?}");
}
In the example, we create five arrays.
let vals: [i32; 5] = [1, 2, 3, 4, 5];
We define an array of five i32 integers. The type is given
explicitly with [i32; 5]. On the right side of the assignment,
the elements are listed inside square brackets and separated with commas.
println!("{vals:?}");
Arrays implement the Debug trait, so we can print them with the
{:?} format specifier. Here the variable name is placed directly
inside the braces.
let words = ["soup", "falcon", "water", "tree"];
We define an array of string slices. The type declaration is omitted; the
compiler infers [&str; 4] from the right side of the
assignment.
let vals2: [i32; 10] = [3; 10];
The [value; N] syntax creates an array of N copies of
the given value. Here all ten elements are set to 3.
let vals3 = [0; 5];
The repeat syntax works with type inference too; we get an array of five zeros.
let squares: [u32; 6] = std::array::from_fn(|i| (i as u32 + 1).pow(2));
The std::array::from_fn function builds an array by calling the
closure for each index. The closure receives the index and returns the element.
$ cargo run -q [1, 2, 3, 4, 5] ["soup", "falcon", "water", "tree"] [3, 3, 3, 3, 3, 3, 3, 3, 3, 3] [0, 0, 0, 0, 0] [1, 4, 9, 16, 25, 36]
Array length
The number of elements of an array is returned by the len
method.
fn main() {
let vals = [1, 2, 3, 4, 5];
let n = vals.len();
println!("The length of the array is {n}");
let words = ["soup", "falcon", "water", "tree"];
println!("The length of the array is {}", words.len());
}
In the example, we determine the size of two arrays. Since the length is part
of the array type, len always returns the same value for a given
array.
$ cargo run -q The length of the array is 5 The length of the array is 4
Accessing array elements
Array elements are accessed with the index notation [i].
fn main() {
let vals = [1, 2, 3, 4, 5];
let n = vals.len();
println!("The first element is: {}", vals[0]);
println!("The second element is: {}", vals[1]);
println!("The last element is: {}", vals[n - 1]);
println!("-----------------------");
println!("The first element is: {:?}", vals.first());
println!("The last element is: {:?}", vals.last());
}
To access an array element, we write the array name followed by square brackets with the index of the element.
println!("The last element is: {}", vals[n - 1]);
The last element has the index len - 1.
println!("The first element is: {:?}", vals.first());
println!("The last element is: {:?}", vals.last());
The first and last methods return an
Option; for an empty array they return
None instead of failing.
$ cargo run -q The first element is: 1 The second element is: 2 The last element is: 5 ----------------------- The first element is: Some(1) The last element is: Some(5)
Out-of-bounds access
Rust checks every index. Accessing an element past the end of an array does not read random memory as in C; the program stops with a panic.
fn main() {
let vals = [1, 2, 3, 4, 5];
let idx = vals.len() + 2;
println!("Accessing index {idx}");
println!("{}", vals[idx]);
}
The index 7 is computed at runtime and is out of the valid range 0..=4. When the index is a constant, the compiler rejects such code already at compile time.
$ cargo run -q Accessing index 7 thread 'main' (13823) panicked at src/main.rs:6:20: index out of bounds: the len is 5 but the index is 7 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The get method
The get method is a safe alternative to the index notation. It
returns an Option: Some with a reference to the
element or subslice, or None when the index is out of bounds.
fn main() {
let vals = [1, 2, 3, 4, 5];
println!("{:?}", vals.get(1));
println!("{:?}", vals.get(1..3));
println!("{:?}", vals.get(10));
match vals.get(7) {
Some(e) => println!("The element is {e}"),
None => println!("Index 7 is out of bounds"),
}
}
In the example, we retrieve a single element, a subslice, and try two invalid indexes.
println!("{:?}", vals.get(1..3));
If we pass a range to get, we receive a subslice. The range
1..3 covers indexes 1 and 2.
match vals.get(7) {
Some(e) => println!("The element is {e}"),
None => println!("Index 7 is out of bounds"),
}
With match, we handle both cases without risking a panic.
$ cargo run -q Some(2) Some([2, 3]) None Index 7 is out of bounds
Modifying array elements
To change the elements, the array must be declared with mut.
fn main() {
let mut vals: [i32; 5] = [0; 5];
println!("{vals:?}");
vals[0] = 5;
vals[1] = 6;
vals[2] = 7;
vals[3] = 8;
vals[4] = 9;
println!("{vals:?}");
for e in vals.iter_mut() {
*e *= 10;
}
println!("{vals:?}");
vals.fill(1);
println!("{vals:?}");
}
We define a mutable array of integers and modify its elements in three different ways.
vals[0] = 5; vals[1] = 6;
Single elements are modified through the index notation.
for e in vals.iter_mut() {
*e *= 10;
}
The iter_mut method gives mutable references to the elements. We
dereference each one with * and multiply it by ten.
vals.fill(1);
The fill method sets all elements to the given value.
$ cargo run -q [0, 0, 0, 0, 0] [5, 6, 7, 8, 9] [50, 60, 70, 80, 90] [1, 1, 1, 1, 1]
Looping over arrays
With a for loop, we can go over the elements of an array.
fn main() {
let vals = [1, 2, 3, 4, 5];
for e in vals {
print!("{e} ");
}
println!();
for i in 0..vals.len() {
print!("{}:{} ", i, vals[i]);
}
println!();
for (i, e) in vals.iter().enumerate() {
print!("vals[{i}]={e} ");
}
println!();
for e in vals.iter().rev() {
print!("{e} ");
}
println!();
}
The program goes through the elements of the array four times.
for e in vals {
print!("{e} ");
}
In the simplest case, we go over the elements one by one. Since
i32 values are copied, the array can still be used after the
loop.
for i in 0..vals.len() {
print!("{}:{} ", i, vals[i]);
}
We can also loop over a range of indexes. This form is less idiomatic; when we
need both the index and the value, enumerate is preferred.
for (i, e) in vals.iter().enumerate() {
print!("vals[{i}]={e} ");
}
The enumerate method creates an iterator of
(index, element) pairs, which we destructure directly in the
loop header.
for e in vals.iter().rev() {
print!("{e} ");
}
The rev method reverses the iterator, so we go from the last
element to the first.
$ cargo run -q 1 2 3 4 5 0:1 1:2 2:3 3:4 4:5 vals[0]=1 vals[1]=2 vals[2]=3 vals[3]=4 vals[4]=5 5 4 3 2 1
Arrays are values
An array of Copy elements is itself Copy. Assigning
it to another variable copies all elements.
fn main() {
let a = [1, 2, 3];
let mut b = a;
b[0] = 100;
println!("a: {a:?}");
println!("b: {b:?}");
println!("a == [1, 2, 3]: {}", a == [1, 2, 3]);
}
After the assignment, a and b are two independent
arrays; changing b does not affect a. Arrays of the
same type can be compared with ==.
$ cargo run -q a: [1, 2, 3] b: [100, 2, 3] a == [1, 2, 3]: true
Arrays and slices
A slice, &[T], is a view into a contiguous sequence
of elements. Functions usually take slices instead of arrays, because a slice
works with arrays of any length, with vectors, and with parts of them.
fn sum(vals: &[i32]) -> i32 {
vals.iter().sum()
}
fn main() {
let small = [1, 2, 3];
let big = [10, 20, 30, 40, 50, 60];
println!("{}", sum(&small));
println!("{}", sum(&big));
println!("{}", sum(&big[1..4]));
println!("{}", sum(&big[..2]));
println!("{}", sum(&big[4..]));
}
The sum function accepts a slice of integers.
println!("{}", sum(&small));
println!("{}", sum(&big));
A reference to an array, &[i32; 3] or
&[i32; 6], is automatically converted to a slice.
println!("{}", sum(&big[1..4]));
println!("{}", sum(&big[..2]));
println!("{}", sum(&big[4..]));
With ranges, we pass only a part of the array: elements 1 to 3, the first two elements, and the elements from index 4 to the end.
$ cargo run -q 6 210 90 30 110
Searching arrays
There are several methods for finding elements in an array.
fn main() {
let words = ["sky", "blue", "cup", "forest", "book"];
println!("{}", words.contains(&"cup"));
println!("{}", words.contains(&"pen"));
println!("{:?}", words.iter().position(|&w| w == "forest"));
println!("{:?}", words.iter().find(|w| w.len() > 4));
let vals = [1, 4, 6, 9, 12, 15];
println!("{:?}", vals.binary_search(&9));
println!("{:?}", vals.binary_search(&10));
}
The example checks whether an array contains a value, finds the index and the value of an element, and runs a binary search.
println!("{}", words.contains(&"cup"));
The contains method returns true if the array
contains the value. It takes a reference to the value.
println!("{:?}", words.iter().position(|&w| w == "forest"));
println!("{:?}", words.iter().find(|w| w.len() > 4));
The position method returns the index of the first element that
satisfies the predicate, and find returns the element itself. Both
return an Option.
println!("{:?}", vals.binary_search(&9));
println!("{:?}", vals.binary_search(&10));
On a sorted array, binary_search returns Ok with the
index of the found element, or Err with the index where the value
could be inserted to keep the order.
$ cargo run -q
true
false
Some(3)
Some("forest")
Ok(3)
Err(4)
Transforming arrays
Arrays can be processed with iterator adapters; the map method
of arrays returns a new array of the same length.
fn main() {
let vals = [1, -2, -3, 4, 5];
let positive: Vec<i32> = vals.iter().filter(|&&e| e > 0).copied().collect();
println!("{positive:?}");
let doubled = vals.map(|e| e * 2);
println!("{doubled:?}");
let total: i32 = vals.iter().sum();
let max = vals.iter().max();
println!("sum: {total}, max: {max:?}");
}
We keep the positive values, double all values, and compute the sum and the maximum.
let positive: Vec<i32> = vals.iter().filter(|&&e| e > 0).copied().collect();
The filter method keeps only the elements for which the closure
returns true. The iterator yields references, and
filter passes a reference to them, hence the
&&e pattern. The copied method turns the
references into values. The number of the remaining elements is not known at
compile time, so the result is collected into
a vector.
let doubled = vals.map(|e| e * 2);
The array map method applies the closure to every element and
returns a new array [i32; 5].
let total: i32 = vals.iter().sum(); let max = vals.iter().max();
The sum and max methods reduce the elements to a
single value. The max method returns an Option,
since an empty array has no maximum.
$ cargo run -q [1, 4, 5] [2, -4, -6, 8, 10] sum: 5, max: Some(5)
Sorting arrays
Mutable arrays can be sorted in place. The sort family of methods
is stable; equal elements keep their original order.
fn main() {
let mut vals = [5, -2, -3, 4, 1, 2];
vals.sort();
println!("{vals:?}");
vals.sort_by(|a, b| b.cmp(a));
println!("{vals:?}");
let mut words = ["war", "atom", "be", "it", "cup", "forest", "book"];
words.sort_by_key(|w| w.len());
println!("{words:?}");
}
The program sorts an array of integers in ascending and descending order and an array of strings by length.
vals.sort();
The sort method sorts the integers in ascending order.
vals.sort_by(|a, b| b.cmp(a));
The sort_by method takes a comparison closure. By swapping the
arguments of cmp, we get descending order.
words.sort_by_key(|w| w.len());
The sort_by_key method sorts by a key computed for each element;
here, the length of the word. Because the sort is stable, words of the same
length stay in their original order: "war" comes before "cup".
$ cargo run -q [-3, -2, 1, 2, 4, 5] [5, 4, 2, 1, -2, -3] ["be", "it", "war", "cup", "atom", "book", "forest"]
Two-dimensional arrays
An array can hold other arrays. The type [[T; C]; R] is an array
of R rows, each holding C elements.
fn main() {
let mut grid = [[0; 4]; 3];
for (r, row) in grid.iter_mut().enumerate() {
for (c, cell) in row.iter_mut().enumerate() {
*cell = r * 4 + c;
}
}
for row in grid {
println!("{row:?}");
}
println!("rows: {}, cols: {}", grid.len(), grid[0].len());
println!("grid[2][1] = {}", grid[2][1]);
}
We create a 3x4 grid of zeros and fill it with consecutive numbers.
let mut grid = [[0; 4]; 3];
The inner [0; 4] is a row of four zeros; the outer expression
repeats the row three times.
for (r, row) in grid.iter_mut().enumerate() {
for (c, cell) in row.iter_mut().enumerate() {
*cell = r * 4 + c;
}
}
With two nested loops, we visit every cell and compute its value from the row and column indexes.
println!("grid[2][1] = {}", grid[2][1]);
An element is accessed with two indexes: the first selects the row, the second the column.
$ cargo run -q [0, 1, 2, 3] [4, 5, 6, 7] [8, 9, 10, 11] rows: 3, cols: 4 grid[2][1] = 9
Source
In this article we have worked with arrays in Rust.
Author
List all Rust tutorials.