What is a Vector in Rust?
A vector is defined using the Vec<T> type, where T
represents the type of elements stored. Vectors are stored in the heap, meaning their size can be adjusted dynamically during runtime.
Why Use Vectors?
- Dynamic Sizing: Vectors can grow or shrink as needed.
- Indexing: You can access elements using zero-based indexing.
- Contiguous Memory: Elements are stored in a continuous block, making access and iteration fast.
- Versatility: They can store any type that implements the Copy or Clone traits.
Creating a Vector
Vectors can be created in several ways:
1. Using Vec: :new
This method creates an empty vector. You can then add elements to it.
fn main() {
let mut numbers: Vec<i32> = Vec::new();
numbers.push(1);
numbers.push(2);
numbers.push(3);
println!("{:?}", numbers); // Output: [1, 2, 3]
}
2. Using the vec! Macro
The vec! macro initializes a vector with predefined values.
fn main() {
let numbers = vec![1, 2, 3];
println!("{:?}", numbers); // Output: [1, 2, 3]
}
Accessing Elements in a Vector
You can access elements in a vector using indexing or the .get() method.
1. Using Indexing
Indexing is straightforward but can cause a runtime panic if the index is out of bounds.
fn main() {
let numbers = vec![10, 20, 30];
println!("{}", numbers[1]); // Output: 20
}
2. Using .get()
The .get() method returns an Option<&T> to handle cases where the index might be invalid.
fn main() {
let numbers = vec![10, 20, 30];
match numbers.get(2) {
Some(value) => println!("Value: {}", value), // Output: Value: 30
None => println!("Index out of bounds!"),
}
}
Modifying a Vector
Vectors must be declared as mut to allow modification.
Adding Elements
- Use .push() to add elements to the end.
- Use .insert(index, value) to insert an element at a specific position.
fn main() {
let mut numbers = vec![1, 2, 3];
numbers.push(4); // Add to the end
numbers.insert(1, 5); // Insert 5 at index 1
println!("{:?}", numbers); // Output: [1, 5, 2, 3, 4]
}
Removing Elements
- Use .pop() to remove the last element.
- Use .remove(index) to remove an element at a specific index.
fn main() {
let mut numbers = vec![1, 2, 3, 4];
numbers.pop(); // Remove the last element
numbers.remove(1); // Remove the element at index 1
println!("{:?}", numbers); // Output: [1, 3]
}
Iterating Over a Vector
Rust provides multiple ways to iterate over a vector.
1. Using a for Loop
fn main() {
let numbers = vec![1, 2, 3];
for num in &numbers {
println!("{}", num); // Output: 1 2 3
}
}
2. Using an Iterator
fn main() {
let numbers = vec![1, 2, 3];
numbers.iter().for_each(|num| println!("{}", num)); // Output: 1 2 3
}
Common Operations on Vectors
Sorting
Sort a vector using .sort().
fn main() {
let mut numbers = vec![3, 1, 2];
numbers.sort();
println!("{:?}", numbers); // Output: [1, 2, 3]
}
Reversing
Reverse a vector using .reverse().
fn main() {
let mut numbers = vec![1, 2, 3];
numbers.reverse();
println!("{:?}", numbers); // Output: [3, 2, 1]
}
Checking for an Element
Use .contains() to check if a vector contains a specific value.
fn main() {
let numbers = vec![1, 2, 3];
println!("{}", numbers.contains(&2)); // Output: true
}
Safety and Ownership in Vectors
Rust enforces strict rules for ownership, borrowing and mutability in vectors:
- You cannot have simultaneous mutable and immutable references.
- Rust ensures memory safety when accessing or modifying vectors.
Example: Borrowing Rules
fn main() {
let mut numbers = vec![1, 2, 3];
let first = &numbers[0];
// numbers.push(4); // Error: Cannot modify while borrowing
println!("{}", first);
}
Performance Considerations
- Vectors automatically allocate more memory as they grow, but this can be costly for large vectors.
- Use .reserve() or .with_capacity() to pre-allocate memory when the size is known.
fn main() {
let mut numbers = Vec::with_capacity(10);
numbers.push(1);
println!("Capacity: {}", numbers.capacity()); // Output: Capacity: 10
}