Arrays in Rust
An array is a fixed-size collection of elements that all have the same type. Unlike a Vec, an array's length is known at compile time and the data lives on the stack, not the heap.
Declaring Arrays
The array type is written as [T; N] where T is the element type and N is the number of elements. Both are fixed parts of the type — [i32; 5] and [i32; 6] are two distinct types.
fn main() {
// Explicit type annotation
let a: [i32; 5] = [1, 2, 3, 4, 5];
// Inferred type (compiler figures out [i32; 4])
let b = [10, 20, 30, 40];
println!("{:?}", a);
println!("{:?}", b);
}Repeat Initializer
Use [value; N] to create an array where every element starts with the same value. This is the most concise way to zero-initialize or fill an array.
fn main() {
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
let ones = [1u8; 8]; // [1, 1, 1, 1, 1, 1, 1, 1]
let trues = [true; 3]; // [true, true, true]
println!("{:?}", zeros);
println!("{:?}", ones);
println!("{:?}", trues);
}Accessing Elements
Array elements are accessed with zero-based index notation a[index]. Rust always performs bounds checking at runtime — accessing an out-of-bounds index causes a panic rather than undefined behaviour.
fn main() {
let a = [10, 20, 30, 40, 50];
println!("first: {}", a[0]);
println!("last: {}", a[4]);
println!("len: {}", a.len());
}fn main() {
let a = [1, 2, 3];
// Safe access — returns None instead of panicking
match a.get(5) {
Some(val) => println!("value: {}", val),
None => println!("index out of bounds"),
}
}Arrays Live on the Stack
Because the size of an array is known at compile time, the compiler reserves exactly the right amount of stack space. No heap allocation occurs. This makes arrays very fast to create and access, but also means they cannot grow or shrink at runtime.
Array Length
.len() returns the number of elements. Because the length is part of the type, the compiler knows it at compile time — .len() is a zero-cost method that reads a constant baked in during compilation.
fn main() {
let a = [1, 2, 3, 4, 5];
println!("length: {}", a.len()); // 5
// Length can be a compile-time constant
const N: usize = 5;
let b: [i32; N] = [0; N];
println!("b has {} elements", b.len());
}Iterating Over Arrays
You can iterate over an array with a for loop. Use for x in &a to borrow elements, or for x in a to consume (move or copy) them.
fn main() {
let a = [10, 20, 30, 40, 50];
// Borrowing iteration — 'a' is still usable after the loop
for x in &a {
print!("{} ", x);
}
println!();
// Moving / copying iteration (works safely for Copy types)
for x in a {
print!("{} ", x);
}
println!();
// Mutable iteration — modify elements in place
let mut b = [1, 2, 3];
for x in b.iter_mut() {
*x *= 10;
}
println!("{:?}", b);
}The Copy Trait and Arrays
An array implements Copy if and only if its element type implements Copy. For Copy arrays, assignment duplicates the data on the stack rather than moving ownership.
fn main() {
// [i32; 3] is Copy
let a = [1, 2, 3];
let b = a; // a is copied, not moved
println!("{:?}", a); // still valid
println!("{:?}", b);
// [String; 2] is NOT Copy — String is not Copy
// let s = [String::from("x"), String::from("y")];
// let t = s; // would move s, making it unusable
}Sorting and Searching
fn main() {
let mut nums = [5, 3, 8, 1, 9, 2];
// In-place ascending sort
nums.sort();
println!("sorted: {:?}", nums);
// Membership check
println!("contains 8: {}", nums.contains(&8));
// Binary search on a sorted array
match nums.binary_search(&8) {
Ok(i) => println!("found 8 at index {}", i),
Err(i) => println!("8 not found; insert position {}", i),
}
// Sort with a custom comparator (descending)
nums.sort_by(|a, b| b.cmp(a));
println!("descending: {:?}", nums);
}2D Arrays
A two-dimensional array is an array of arrays. The type is written as [[T; cols]; rows]. Elements are stored in row-major order, which matches how most algorithms naturally iterate.
fn main() {
// 3x3 matrix
let matrix: [[i32; 3]; 3] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// Access element at row 1, col 2
println!("matrix[1][2] = {}", matrix[1][2]); // 6
// Iterate over all elements
for row in &matrix {
for val in row {
print!("{:3}", val);
}
println!();
}
}Slices from Arrays
You can borrow a portion of an array as a slice (&[T]) without copying the data. This is how arrays and slice-based APIs interoperate seamlessly.
fn print_slice(s: &[i32]) {
println!("{:?}", s);
}
fn main() {
let a = [1, 2, 3, 4, 5];
let full = &a[..]; // whole array as &[i32]
let middle = &a[1..4]; // [2, 3, 4]
print_slice(full);
print_slice(middle);
// Arrays coerce to &[T] automatically in function calls
print_slice(&a);
}Array vs Vec
Feature | Array | Vec |
|---|---|---|
Size | Fixed at compile time | Dynamic at runtime |
Storage | Stack | Heap |
Allocation | None | Heap allocation |
Resizable | No | Yes (push, pop, extend) |
Performance | Slightly faster — no indirection | Slight overhead for heap pointer |
Best for | Small, known-size collections | Unknown or variable-size data |
When to Use Arrays vs `Vec`
Use an array when the number of elements is known at compile time and will never change.
Use a
Vecwhen you need to add or remove elements at runtime, or when the size is only known at runtime.Arrays are ideal for fixed-size buffers, lookup tables, day-of-week arrays, RGB triplets, and similar.
If unsure, start with a
Vec— it is more flexible and the performance difference is usually negligible.
Practical Examples
fn main() {
// Day name lookup table
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
println!("Day 3 is {}", days[2]); // Wed
// Compute the average of a fixed dataset
let temps: [f64; 7] = [22.1, 19.5, 25.3, 21.0, 18.7, 23.4, 20.8];
let avg = temps.iter().sum::<f64>() / temps.len() as f64;
println!("Average temperature: {:.1}", avg);
// Count even numbers
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let even_count = nums.iter().filter(|&&x| x % 2 == 0).count();
println!("Even numbers: {}", even_count);
}Summary
Arrays have type
[T; N]— element type and length are both fixed at compile time.Use
[value; N]to initialize every element with the same value.Array data lives on the stack — no heap allocation, very fast for small fixed-size collections.
Out-of-bounds access panics at runtime; use
.get(i)for safeOption-returning access.Arrays implement
Copywhen their element type implementsCopy.Use
&a[start..end]to borrow a sub-range as a&[T]slice.Arrays coerce to
&[T]automatically, so slice-based functions accept both arrays andVecs.Choose arrays for fixed compile-time sizes; choose
Vecfor dynamic runtime sizes.