Rust - Array
Rust - Arrays, Vectors, and Slices
Section titled “Rust - Arrays, Vectors, and Slices”In this chapter, we will learn about Rust’s primary collection types for sequences of data. We’ll start with the array, a simple, fixed-size collection, and then introduce more flexible and commonly used types: vectors and slices.
A simple variable can only hold one value at a time. To store a list of values, like the top 10 scores in a game, we need a data structure that can hold multiple values. Arrays are the most basic way to do this.
Understanding Arrays [T; N]
Section titled “Understanding Arrays [T; N]”An array in Rust is a collection of multiple values of the same type (T) with a fixed length (N).
- Fixed Size: Once an array is declared, its size can never be changed. This is a key difference from more flexible types like Vectors.
- Homogeneous: All elements in an array must have the same data type.
- Stack Allocated: Arrays are allocated on the stack, which makes them very fast to access. This is ideal when you know the exact number of elements you need at compile time.
- Sequential Memory: The elements of an array are stored next to each other in memory, allowing for efficient, predictable access.
- Indexed Access: Elements are accessed by their index, starting from 0.
Declaring and Initializing Arrays
Section titled “Declaring and Initializing Arrays”Here are the common ways to declare and initialize an array in Rust.
Syntax
Section titled “Syntax”// Syntax 1: Type inference// The compiler infers the type and size.let my_array = [1, 2, 3, 4, 5];
// Syntax 2: Explicit type and size annotation// [type; size]let my_array_typed: [i32; 5] = [1, 2, 3, 4, 5];
// Syntax 3: Initialize all elements to the same value// [default_value; size]let all_zeros = [0; 10]; // Creates an array of 10 zeros.Example: Basic Array Usage
Section titled “Example: Basic Array Usage”This example shows how to create an array, access an element, and get its length. We use the debug format {:?} to print the entire array.
fn main() { // Declare an array of 4 integers. let numbers: [i32; 4] = [10, 20, 30, 40];
// Print the entire array. println!("Array: {:?}", numbers);
// Get the length of the array. println!("Array size: {}", numbers.len());
// Access a single element by index. let first_element = numbers[0]; println!("The first element is: {}", first_element);}Output
Section titled “Output”Array: [10, 20, 30, 40]Array size: 4The first element is: 10Iterating Through an Array
Section titled “Iterating Through an Array”It’s common to perform an operation on every element in an array. Idiomatic Rust uses iterators for this, which are safer and more expressive than manual index-based loops.
Example: Using an Iterator
Section titled “Example: Using an Iterator”The .iter() method creates an iterator that lets you loop over each element. To get both the index and the value, use .enumerate().
fn main() { let weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];
println!("Iterating over values:"); for day in weekdays.iter() { println!("Day: {}", day); }
println!("\nIterating with index and value:"); for (index, day) in weekdays.iter().enumerate() { println!("Day {}: {}", index + 1, day); }}Output
Section titled “Output”Iterating over values:Day: MondayDay: TuesdayDay: WednesdayDay: ThursdayDay: Friday
Iterating with index and value:Day 1: MondayDay 2: TuesdayDay 3: WednesdayDay 4: ThursdayDay 5: FridayMutable Arrays
Section titled “Mutable Arrays”By default, variables in Rust are immutable. To modify an array’s elements, you must declare it with the mut keyword.
fn main() { let mut scores: [i32; 5] = [88, 92, 79, 100, 95]; println!("Original scores: {:?}", scores);
// Update the score at index 2 scores[2] = 85; println!("Updated scores: {:?}", scores);}Output
Section titled “Output”Original scores: [88, 92, 79, 100, 95]Updated scores: [88, 92, 85, 100, 95]Beyond Arrays: Vectors and Slices
Section titled “Beyond Arrays: Vectors and Slices”Arrays are useful, but their fixed size is limiting. For most real-world scenarios, you’ll use Vectors and Slices.
- Vector (
Vec<T>): A growable, resizable list. It’s stored on the heap. This is the type you’ll use most often when you need a list of items. - Slice (
&[T]): A ‘view’ or ‘reference’ into a contiguous sequence of elements. Slices allow you to safely and efficiently access a portion of an array or a vector without copying data. They are incredibly powerful for writing flexible functions.
Flexible Functions with Slices
Section titled “Flexible Functions with Slices”Instead of writing a function that only accepts an array of a specific size (like [i32; 3]), it’s a best practice to accept a slice (&[i32]). This makes your function more versatile, as it can now work with arrays of any size, and also with vectors.
// This function takes a slice of integers, making it very flexible.fn sum_slice(numbers: &[i32]) -> i32 { let mut total = 0; for &num in numbers { total += num; } total}
fn main() { let arr1: [i32; 5] = [1, 2, 3, 4, 5]; let arr2: [i32; 3] = [10, 20, 30]; let vec1: Vec<i32> = vec![100, 200, 300];
// We can pass a reference to the whole array, which is coerced into a slice. println!("Sum of arr1: {}", sum_slice(&arr1));
// Or a slice of a portion of the array. println!("Sum of part of arr1: {}", sum_slice(&arr1[1..4])); // Elements at index 1, 2, 3
// The same function works for arr2! println!("Sum of arr2: {}", sum_slice(&arr2));
// And it works for vectors too! println!("Sum of vec1: {}", sum_slice(&vec1));}Output
Section titled “Output”Sum of arr1: 15Sum of part of arr1: 9Sum of arr2: 60Sum of vec1: 600Common Pitfalls and Best Practices
Section titled “Common Pitfalls and Best Practices”- Out-of-Bounds Access: Accessing an array with an index that doesn’t exist will cause your program to
panic. For example,numbers[10]on an array of size 5. Always check lengths or use iterators to avoid this. - Choosing the Right Type: Use an array
[T; N]when you know the number of elements will not change and you want stack performance. Use a vectorVec<T>for everything else. Use slices&[T]in function arguments to make them more flexible. - Constants for Size: An array’s size must be known at compile time. If you need to use a named value for the size, it must be a
const.
fn main() { const ARRAY_SIZE: usize = 20; // This is valid because ARRAY_SIZE is a constant. let my_array = [0; ARRAY_SIZE]; println!("Element 10 is {}", my_array[10]);}