Vectors in Rust
Vectors are Rust's most common collection. They store multiple values of the same type in contiguous memory. Think of them as dynamic arrays that can grow and shrink as needed.
You can create a vector in two ways: with Vec::new() or the handy vec! macro. Both give you an empty vector to start with.
fn main() {
let mut numbers: Vec<i32> = Vec::new();
let names = vec!["Alice", "Bob", "Charlie"];
numbers.push(1);
numbers.push(2);
numbers.push(3);
println!("Numbers: {:?}", numbers);
println!("Names: {:?}", names);
}
Try it Yourself ->
Adding and Removing Elements
Use push() to add elements and pop() to remove them. pop() returns an Option<T> because the vector might be empty.
fn main() {
let mut colors = vec!["red", "green"];
colors.push("blue");
colors.push("yellow");
println!("Colors: {:?}", colors);
println!("Length: {}", colors.len());
println!("Is empty: {}", colors.is_empty());
let last = colors.pop();
println!("Popped: {:?}", last);
println!("Colors after pop: {:?}", colors);
}
Try it Yourself ->
Accessing Elements
You can access elements by index or with get(). The get() method returns an Option, which is safer because it handles out-of-bounds access gracefully.
fn main() {
let scores = vec![85, 92, 78, 95, 88];
let first = scores[0];
println!("First score: {}", first);
let second = scores.get(1);
match second {
Some(score) => println!("Second score: {}", score),
None => println!("No second score"),
}
let out_of_bounds = scores.get(10);
println!("Out of bounds: {:?}", out_of_bounds);
}
Try it Yourself ->
Iterating Over Vectors
You can iterate over vectors in several ways. The for loop is the most common, but you can also use iterators for more control.
fn main() {
let fruits = vec!["apple", "banana", "cherry", "date"];
for fruit in &fruits {
println!("I love {}", fruit);
}
for (index, fruit) in fruits.iter().enumerate() {
println!("{}. {}", index + 1, fruit);
}
}
Try it Yourself ->
Vectors of Structs
Vectors work great with custom structs. This is how you'd store a collection of related data in real programs.
#[derive(Debug)]
struct Student {
name: String,
grade: f64,
}
fn main() {
let students = vec![
Student { name: "Alice".to_string(), grade: 3.8 },
Student { name: "Bob".to_string(), grade: 3.5 },
Student { name: "Charlie".to_string(), grade: 3.9 },
];
for student in &students {
println!("{}: {}", student.name, student.grade);
}
}
Try it Yourself ->
Filtering and Transforming
Rust's iterator methods make it easy to filter and transform vectors. These methods are lazy, meaning they only compute when you need the results.
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evens: Vec<_> = numbers.iter()
.filter(|&&x| x % 2 == 0)
.cloned()
.collect();
let doubled: Vec<_> = numbers.iter()
.map(|&x| x * 2)
.collect();
let sum: i32 = numbers.iter().sum();
println!("Even numbers: {:?}", evens);
println!("Doubled: {:?}", doubled);
println!("Sum: {}", sum);
}
Try it Yourself ->
Why Vectors Matter
Vectors are Rust's go-to collection for good reason. They're fast, flexible, and integrate beautifully with Rust's ownership system. Most of the time, a vector is exactly what you need when you want a list of things.
Understanding vectors is foundational because many other Rust concepts build on them. Once you're comfortable with vectors, collections like HashMaps and iterators become much easier to grasp.