Collections in Rust are special data structures that can hold multiple values together. collections can grow or shrink dynamically as you add or remove elements. They allow you organize and manage groups of data efficiently.
Rust provides several commonly used collections:
- Vec<T> (Vector)
- String
- HashMap<K, V>
- HashSet<T>
- VecDeque<T>
- LinkedList<T>
- BinaryHeap<T>
Each collection is optimized for specific use cases, and selecting the right one is crucial for performance.
1. Vec<T>: The Vector
A Vec<T> (vector) is like a flexible array that can grow or shrink. All elements must be of the same type, and you can access them by index.
You can also add new elements using push, remove the last element using pop, and loop through the vector easily.
Example: Basic Vector Usage
fn main() {
// Create a mutable vector to store integers
let mut scores: Vec<i32> = Vec::new();
// Add some scores
scores.push(50);
scores.push(75);
scores.push(90);
println!("All Scores: {:?}", scores); // Print all scores
// Remove the last score
let removed = scores.pop();
println!("Removed score: {:?}", removed);
// Print remaining scores one by one
for score in &scores {
println!("Score: {}", score);
}
}
Explanation of this code:
- Vec::new() creates an empty vector.
- push() adds elements at the end.
- pop() removes the last element and returns it.
Output:

2. String: Owned String Type
A String in Rust is a dynamic, growable text type stored on the heap. You can add text, replace parts, or use many built-in methods for string manipulation.
Simple example of String Operations
fn main() {
// Create a mutable String
let mut message = String::from("Good morning");
// Add more text at the end
message.push_str(", students!");
println!("Message: {}", message); // Output: Good morning,
3. HashMap<K, V>: Key-Value Store
A HashMap in Rust is a collection of key-value pairs, where each key is unique and maps to a value. It is useful when you want fast lookups, insertions, or updates using keys.
Keys must implement the Eq and Hash traits so Rust can compare and locate them efficiently.
Example: Using HashMap
use std::collections::HashMap;
fn main() {
// Create a new HashMap to store fruits and their quantities
let mut fruit_inventory = HashMap::new();
// Add items
fruit_inventory.insert("Apple", 10);
fruit_inventory.insert("Banana", 25);
fruit_inventory.insert("Orange", 15);
// Check quantity of a specific fruit
if let Some(quantity) = fruit_inventory.get("Banana") {
println!("Bananas in stock: {}", quantity);
}
// Iterate over all items
for (fruit, quantity) in &fruit_inventory {
println!("{}: {} pcs", fruit, quantity);
}
}
Output:
Bananas in stock: 25
Apple: 10 pcs
Banana: 25 pcs
Orange: 15 pcs
4. HashSet<T>: Unique Values
A HashSet in Rust is a collection that stores unique values only. If you try to add a duplicate, it will automatically be ignored. It is useful when you need to track distinct items or perform set operations like union, intersection, or difference.
Simple HashSet Example:
use std::collections::HashSet;
fn main() {
// Create a HashSet to store unique student names
let mut students = HashSet::new();
// Add names
students.insert("Alice");
students.insert("Bob");
students.insert("Charlie");
students.insert("Alice"); // Duplicate, will be ignored
// Print all unique names
println!("Students in class: {:?}", students);
// Check if a student exists
if students.contains("Bob") {
println!("Bob is in the class.");
}
}
Output:
Students in class: {"Alice", "Bob", "Charlie"}
Bob is in the class.
5. VecDeque<T>: Double-Ended Queue
A VecDeque in Rust is a double-ended queue, which allows you to add or remove elements from both the front and the back efficiently. It is better than a Vec when you need frequent operations at both ends, making it perfect for queues or deques.
VecDeque Example:
use std::collections::VecDeque;
fn main() {
// Create a double-ended queue to manage tasks
let mut tasks = VecDeque::new();
// Add tasks at the back
tasks.push_back("Task 1");
tasks.push_back("Task 2");
// Add a high-priority task at the front
tasks.push_front("Urgent Task");
println!("Current task queue: {:?}", tasks);
// Remove tasks from front as they get completed
if let Some(next_task) = tasks.pop_front() {
println!("Working on: {}", next_task);
}
println!("Remaining tasks: {:?}", tasks);
}
Output:

6. LinkedList<T>: Doubly Linked List
A LinkedList in Rust is a doubly linked list, where elements (nodes) are connected using pointers.
LinkedList Examples:
use std::collections::LinkedList;
fn main() {
// Create a LinkedList to manage a playlist
let mut playlist = LinkedList::new();
// Add songs at the end
playlist.push_back("Song A");
playlist.push_back("Song B");
// Add a song at the beginning
playlist.push_front("Intro Song");
println!("Current playlist: {:?}", playlist);
// Remove the first song (like playing it)
if let Some(current_song) = playlist.pop_front() {
println!("Now playing: {}", current_song);
}
println!("Remaining playlist: {:?}", playlist);
}
Output:

- LinkedList stores elements in nodes linked by pointers, not in contiguous memory like Vec.
- You can add/remove from the front, back, or middle efficiently.
7. BinaryHeap<T>: Priority Queue
A BinaryHeap in Rust is a priority queue that automatically keeps elements in a max-heap order (largest element at the top by default). It’s useful when you want fast access to the highest-priority item.
Example: Using BinaryHeap
use std::collections::BinaryHeap;
fn main() {
// Create a priority queue for tasks based on importance
let mut tasks = BinaryHeap::new();
// Push tasks with priority values (higher = more important)
tasks.push(5); // Medium priority task
tasks.push(10); // High priority task
tasks.push(2); // Low priority task
println!("Tasks in priority order:");
// Pop tasks by highest priority first
while let Some(task) = tasks.pop() {
println!("Processing task with priority: {}", task);
}
}
Output of this code:
