Rust: Common Rust Collections
Last updated: 2026-08-26
VecDeque is a double-ended queue that supports efficient insertion and deletion at both ends; BTreeMap is an ordered key-value map that supports range queries. They are "specialized" members of Rust's collection library.
If a Vec is like “waiting in line to get on a bus” (you can only join at the end of the line), then a VecDeque is like “someone cutting in line” (you can enter and exit from either end). If a HashMap is like a “dictionary” (random lookups), then a BTreeMap is like a “contact list” (arranged in alphabetical order, and you can flip to a specific page to start reading).
1. What You'll Learn
- VecDeque:
push_front/pop_backOperations on a Double-Ended Queue - BTreeMap: Ordered Key-Value Storage and Range Queries
range - Use Cases and Limitations of LinkedLists
- Performance Comparison of Four Collection Types (Vec / VecDeque / HashMap / BTreeMap)
- Set Selection Strategy: Choose the Right Data Structure Based on the Scenario
2. Conceptual Diagrams
The following Mermaid comparison diagram illustrates the differences between the VecDeque double-ended queue and the BTreeMap B-tree map data structures:
graph LR
subgraph VecDeque["VecDeque Double-ended queue"]
direction LR
V1["pop_front O(1)"] --> V2["Element1"]
V2 --> V3["Element2"]
V3 --> V4["..."]
V4 --> V5["ElementN"]
V5 --> V6["push_back O(1)"]
V7["push_front O(1)"] -.-> V2
end
subgraph BTreeMap["BTreeMap BOrdered Map of Trees"]
direction TB
B0["Root Node"] --> B1["Left Branch<br/>Keys: A-M"]
B0 --> B2["Right Branch<br/>Keys: N-Z"]
B1 --> B3["(Alice, 95)"]
B1 --> B4["(Bob, 72)"]
B2 --> B5["(Charlie, 87)"]
B2 --> B6["(David, 91)"]
end
VecDeque -.-> |Comparison of Data Structures| BTreeMap
3. The Story of a Queueing and Ticket System
(1) Problem: Simulating a queue using Vec, but both cutting in line and leaving the queue are slow
Tom (Tom) is developing a queue management system for restaurants. Customers pick up a number when they arrive, and the server calls out the number to seat them.
At first, he implemented it using Vec:
let mut queue = Vec::new();
queue.push("Customer A"); // A Get a ticket,At the back of the line
queue.push("Customer B"); // B Get a ticket,At the back of the line
queue.push("Customer C"); // C Get a ticket,At the back of the line
// Take a seat when your number is called:Remove from the front of the line
let first = queue.remove(0); // O(n) —— All elements that follow must be moved forward!
println!("{} Take a seat", first);
Vec's
remove(0)is an O(n) operation—after removing the first element, all remaining elements must be shifted forward by one position. If there are 10,000 people in line, each time a number is called, 9,999 elements must be shifted. This is clearly not suitable for a production system.
(2) More complex requirements: VIP priority + search by number
To make matters worse, the restaurant manager said:
- VIP customers can cut in line to the front of the line
- Customer service representatives need to quickly look up customer information by ticket number
- Sometimes it is necessary to look up "customers who took a number between 2:00 p.m. and 3:00 p.m."
Vec just can't handle these requirements at all.
(3) Solutions for Rust Collections
use std::collections::VecDeque;
use std::collections::BTreeMap;
fn main() {
// VecDeque:Accessible from both the front and the back
let mut queue = VecDeque::new();
queue.push_back("Customer A"); // Enter at the back of the line
queue.push_back("Customer B");
queue.push_front("VIP"); // VIP Cutting in line at the front of the line!
println!("Next, please take a seat.: {}", queue.pop_front().unwrap()); // VIP
println!("Next: {}", queue.pop_front().unwrap()); // Customer A
// BTreeMap:Ordered Key-Value Store
let mut customers = BTreeMap::new();
customers.insert(1001, "Alice");
customers.insert(1003, "Bob");
customers.insert(1002, "Charlie");
// Automatically Sort and Output Buttons
for (id, name) in &customers {
println!("Get a ticket #{}: {}", id, name);
}
// Output: #1001: Alice, #1002: Charlie, #1003: Bob
}
Operations at both ends of a VecDeque are O(1), while a BTreeMap automatically sorts keys and supports range queries. The former solves the "queuing" problem, and the latter solves the "ordered search" problem.
4. Core Concepts
(1) Comparison of Four Set Structures
graph TB
A[Rust Collection Library] --> B[Vec: Contiguous Arrays]
A --> C[VecDeque: Double-ended queue]
A --> D[HashMap: Hash Table]
A --> E[BTreeMap: B Ordered Map of Trees]
B --> F["push / pop: O(1) Amortized"]
B --> G["insert(0) / remove(0): O(n)"]
C --> H["push_front / pop_front: O(1)"]
C --> I["push_back / pop_back: O(1)"]
D --> J["Insert / Search: O(1) Amortized"]
D --> K["Disorder —— No order guarantee"]
E --> L["Insert / Search: O(log n)"]
E --> M["Orderly —— Support for Range Queries range"]
(2) Comparison of the Performance of Four Types of Sets
| Operation | Vec | VecDeque | HashMap | BTreeMap |
|---|---|---|---|---|
| Tail insertion | O(1) amortized | O(1) | O(1) amortized | O(log n) |
| Insertion at the front | O(n) | O(1) | — | — |
| Access by index | O(1) | O(1) | — | — |
| Key lookup | — | — | O(1) amortized | O(log n) |
| Range Query | Not supported | Not supported | Not supported | O(log n + k) |
| Ordered traversal | Must be sorted | Must be sorted | Unordered | O(n) (sorted) |
| Memory usage | Low | Low | Medium | Medium |
(3) Selection Strategy
| Scenario | Recommended Collection | Reason |
|---|---|---|
| Queue / Buffer (operations on both ends) | VecDeque | O(1) on both ends—nothing is more suitable than this |
| Requires key-sorted traversal | BTreeMap | Automatically sorted; no additional sorting steps required |
| Range Queries Required | BTreeMap | The only collection that supports range queries |
| Only requires tail operations | Vec | Simpler and more memory-efficient |
| Requires only key-value lookups | HashMap | O(1) lookups, faster than BTreeMap |
| Frequent insertion and deletion of middle elements | LinkedList | Theoretical O(1), but rarely used in practice |
(4) Quick Reference for Common Methods of VecDeque and BTreeMap
| Set | Method | Description | Time Complexity |
|---|---|---|---|
| VecDeque | push_front(val) |
Insert at the front | O(1) |
| VecDeque | push_back(val) |
Insert at the end | O(1) |
| VecDeque | pop_front() |
Pop the first element of the array | O(1) |
| VecDeque | pop_back() |
Pop from the end | O(1) |
| VecDeque | front() / back() |
View first and last elements | O(1) |
| VecDeque | len() / is_empty() |
Length Query | O(1) |
| BTreeMap | insert(k, v) |
Insert key-value pair | O(log n) |
| BTreeMap | get(&k) |
Key-based search | O(log n) |
| BTreeMap | remove(&k) |
Delete by key | O(log n) |
| BTreeMap | range(start..=end) |
Range Query | O(log n + k) |
| BTreeMap | first_key_value() |
Least key-value pair | O(log n) |
| BTreeMap | last_key_value() |
Maximum key-value pair | O(log n) |
5. Set Examples
▶ Example 1: VecDeque Double-Ended Queue — Queueing and Ticket Issuance System (Difficulty ⭐)
Output:
Current number of people in line: <queue.len()>
At the head of the line: <queue.front()>, Bringing up the rear: <queue.back()>
Call Order:
<customer> Take a seat
// ============================================
// VecDeque:Basic Operations on a Double-Ended Queue
// ============================================
use std::collections::VecDeque;
fn main() {
let mut queue: VecDeque<&str> = VecDeque::new();
// Regular customers enter from the back of the line
queue.push_back("Alice");
queue.push_back("Bob");
queue.push_back("Charlie");
// VIP A customer cut in line from the front of the line
queue.push_front("VIP-David");
queue.push_front("VIP-Eve");
println!("Current number of people in line: {}", queue.len());
println!("At the head of the line: {:?}, Bringing up the rear: {:?}", queue.front(), queue.back());
// Take a seat when your number is called:Remove from the front of the queue
println!("\nCall Order:");
while let Some(customer) = queue.pop_front() {
println!(" {} Take a seat", customer);
}
}
Output:
=== Transcript(Sort by Name)===
<name>: <score>
Alice the results: <scores.get("Alice").unwrap()>
Eve There are discrepancies in the scores
Eve No results yet
First: <scores.first_key_value()>
The last one: <scores.last_key_value()>
push_front()andpush_back()are both O(1) operations.front()andback()return references to the front and back of the queue, respectively, without removing the elements.pop_front()removes and returns the element at the front of the queue.
▶ Example 2: BTreeMap—Ordered Key-Value Storage: Grade Rankings (Difficulty ⭐⭐)
Output:
=== Transcript(Sort by Name)===
<name>: <score>
Alice the results: <scores.get("Alice").unwrap()>
Eve There are discrepancies in the scores
Eve No results yet
// ============================================
// BTreeMap:Automatic Button Sorting
// ============================================
use std::collections::BTreeMap;
fn main() {
let mut scores = BTreeMap::new();
scores.insert("Charlie", 87);
scores.insert("Alice", 95);
scores.insert("Bob", 72);
scores.insert("David", 91);
// BTreeMap Button(Alphabetical order)Automatic Sorting
println!("=== Transcript(Sort by Name)===");
for (name, score) in &scores {
println!(" {}: {}", name, score);
}
// Search by Key
println!("\nAlice the results: {}", scores.get("Alice").unwrap());
// Check for the presence of
if scores.contains_key("Eve") {
println!("Eve There are discrepancies in the scores");
} else {
println!("Eve No results yet");
}
// Get the first and last entries
println!("\nFirst: {:?}", scores.first_key_value());
println!("The last one: {:?}", scores.last_key_value());
}
Output:
=== Transcript(Sort by Name)===
Alice: 95
Bob: 72
Charlie: 87
David: 91
Alice the results: 95
Eve No results yet
First: Some(("Alice", 95))
The last one: Some(("David", 91))
BTreeMap automatically sorts keys in
Ordorder. The default sort order for Strings is lexicographical, so "Alice" comes first and "David" comes last.first_key_value()andlast_key_value()provide efficient access to the first and last elements.
▶ Example 3: BTreeMap Range Query—Date Range Query (Difficulty ⭐⭐)
Output:
=== July 3 ~ July 7 Orders ===
<date>: $<amount>
=== July 5 and After Orders ===
<date>: $<amount>
// ============================================
// BTreeMap Range Query:range Methods
// ============================================
use std::collections::BTreeMap;
fn main() {
// Simulated Order System:Date -> Order Amount
let mut orders = BTreeMap::new();
orders.insert("2026-07-01", 120);
orders.insert("2026-07-03", 85);
orders.insert("2026-07-05", 200);
orders.insert("2026-07-07", 150);
orders.insert("2026-07-10", 95);
// Search orders placed between July 3 and July 7 (inclusive)
println!("=== July 3 ~ July 7 Orders ===");
for (date, amount) in orders.range("2026-07-03"..="2026-07-07") {
println!(" {}: ${}", date, amount);
}
// Search all orders on or after July 5
println!("\n=== July 5 and After Orders ===");
for (date, amount) in orders.range("2026-07-05"..) {
println!(" {}: ${}", date, amount);
}
// Total Amount
let total: i32 = orders.range("2026-07-03"..="2026-07-07")
.map(|(_, amount)| amount)
.sum();
println!("\nJuly 3 ~ July 7 Total Amount: ${}", total);
}
Output:
=== July 3 ~ July 7 Orders ===
2026-07-03: $85
2026-07-05: $200
2026-07-07: $150
=== July 5 and After Orders ===
2026-07-05: $200
2026-07-07: $150
2026-07-10: $95
July 3 ~ July 7 Total Amount: $435
range()is a method unique to BTreeMap. It accepts a Range expression (..for an open interval,..=for a closed interval, andstart..for a half-open interval) and returns an iterator. This is something HashMap cannot do.
▶ Example 4: Performance Comparison of Four Types of Sets (Difficulty ⭐⭐⭐)
Output:
Vec Insert at the end: <start.elapsed()>
VecDeque Insert at the end: <start.elapsed()>
HashMap Insert: <start.elapsed()>
BTreeMap Insert: <start.elapsed()>
--- Search Performance ---
Vec Linear Search: <start.elapsed()>
HashMap Search: <start.elapsed()>
BTreeMap Search: <start.elapsed()>
// ============================================
// Comparison of the Performance of Four Types of Sets:Insert 10000 element
// ============================================
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::time::Instant;
fn main() {
let n = 10_000;
// Vec Insert at the end
let start = Instant::now();
let mut vec = Vec::new();
for i in 0..n {
vec.push(i);
}
println!("Vec Insert at the end: {:?}", start.elapsed());
// VecDeque Insert at the end
let start = Instant::now();
let mut deque = VecDeque::new();
for i in 0..n {
deque.push_back(i);
}
println!("VecDeque Insert at the end: {:?}", start.elapsed());
// HashMap Insert
let start = Instant::now();
let mut hmap = HashMap::new();
for i in 0..n {
hmap.insert(i, i);
}
println!("HashMap Insert: {:?}", start.elapsed());
// BTreeMap Insert
let start = Instant::now();
let mut bmap = BTreeMap::new();
for i in 0..n {
bmap.insert(i, i);
}
println!("BTreeMap Insert: {:?}", start.elapsed());
// Search for Performance Comparisons
println!("\n--- Search Performance ---");
let start = Instant::now();
let _ = vec.contains(&9999);
println!("Vec Linear Search: {:?}", start.elapsed());
let start = Instant::now();
let _ = hmap.get(&9999);
println!("HashMap Search: {:?}", start.elapsed());
let start = Instant::now();
let _ = bmap.get(&9999);
println!("BTreeMap Search: {:?}", start.elapsed());
}
Output:
Vec Insert at the end: 78.2µs
VecDeque Insert at the end: 82.1µs
HashMap Insert: 1.2ms
BTreeMap Insert: 2.8ms
--- Search Performance ---
Vec Linear Search: 42.5µs
HashMap Search: 138ns
BTreeMap Search: 312ns
The performance data shows that HashMap is the fastest for lookups (O(1)), followed by BTreeMap (O(log n)), while Vec is the slowest for linear lookups (O(n)). For insertions, tail insertion is fastest with Vec and VecDeque, and slowest with BTreeMap. This confirms that there is “no silver bullet”—the choice of collection depends on the specific use case.
▶ Example 5: Comprehensive Exercise—Task Scheduling System (Difficulty ⭐⭐⭐)
Output:
=== FIFO Queue Processing ===
[P<task.priority>] #<task.id>: <task.name>
=== Sort by priority ===
P<task_list>: <pri>
=== High-Priority Tasks (P0-P1) ===
P<task_list>: <pri>
// ============================================
// Comprehensive Example:VecDeque Queue + BTreeMap Priority
// ============================================
use std::collections::{VecDeque, BTreeMap};
#[derive(Debug, Clone)]
struct Task {
id: u32,
name: String,
priority: u8,
}
fn main() {
let mut queue: VecDeque<Task> = VecDeque::new();
queue.push_back(Task { id: 1, name: "Check email".into(), priority: 3 });
queue.push_back(Task { id: 2, name: "Fix bug #42".into(), priority: 1 });
queue.push_back(Task { id: 3, name: "Write report".into(), priority: 2 });
queue.push_back(Task { id: 4, name: "Urgent deploy".into(), priority: 0 });
println!("=== FIFO Queue Processing ===");
while let Some(task) = queue.pop_front() {
println!("[P{}] #{}: {}", task.priority, task.id, task.name);
}
let mut priority_map: BTreeMap<u8, Vec<String>> = BTreeMap::new();
let tasks = vec![
("Check email", 3u8), ("Fix bug #42", 1), ("Write report", 2),
("Urgent deploy", 0), ("Code review", 1), ("Team meeting", 3),
("Security patch", 0), ("Update docs", 2),
];
for (name, pri) in tasks {
priority_map.entry(pri).or_insert_with(Vec::new).push(name.to_string());
}
println!("\n=== Sort by priority ===");
for (pri, task_list) in &priority_map {
println!("P{}: {:?}", pri, task_list);
}
println!("\n=== High-Priority Tasks (P0-P1) ===");
for (pri, task_list) in priority_map.range(0..=1) {
println!("P{}: {:?}", pri, task_list);
}
let mut history: VecDeque<String> = VecDeque::with_capacity(3);
for i in 0..5 {
history.push_back(format!("task_{}", i));
if history.len() > 3 {
history.pop_front();
}
}
println!("\nLast 3 history entries: {:?}", history);
}
Output:
=== FIFO Queue Processing ===
[P3] #1: Check email
[P1] #2: Fix bug #42
[P2] #3: Write report
[P0] #4: Urgent deploy
=== Sort by priority ===
P0: ["Urgent deploy", "Security patch"]
P1: ["Fix bug #42", "Code review"]
P2: ["Write report", "Update docs"]
P3: ["Check email", "Team meeting"]
=== High-Priority Tasks (P0-P1) ===
P0: ["Urgent deploy", "Security patch"]
P1: ["Fix bug #42", "Code review"]
Last 3 history entries: ["task_2", "task_3", "task_4"]
Use a VecDeque as a FIFO queue (
push_back+pop_front); BTreeMap groups items by priority and automatically sorts them;range(0..=1)displays only high-priority tasks; the fixed-capacity VecDeque implements a "latest N entries" history.
❓ FAQ
LinkedList rarely used in Rust?LinkedList incurs additional pointer overhead, and it is cache-unfriendly.range operation?next() and nth() to implement cursor-based pagination.📖 Summary
- VecDeque doubly-linked list:
push_front/push_back/pop_front/pop_backare all O(1) - BTreeMap Ordered Map: Keys are automatically sorted; supports
range()range queries - LinkedList is rarely used in Rust—it’s cache-unfriendly, and its actual performance is inferior to VecDeque
- HashMap has O(1) lookup time but is unordered; BTreeMap has O(log n) lookup time but is ordered
- Set Selection Strategy: Select the correct set based on the operation mode (tail/both ends/search/range)
- VecDeque and BTreeMap are underrated gems in std::collections—in many scenarios, they are more suitable than Vec and HashMap
📝 Exercises
- Difficulty ⭐: Implement an "undo history" using VecDeque. Record each operation using
push_back, and when undoing, remove the most recent operation usingpop_back. Simulate 3 operations followed by 2 undo operations, and print the contents of each operation. - Difficulty ⭐⭐: Implement a "Student Grade Management System" using a B-Tree Map. Insert the names and grades of 5 students, and print the rankings in descending order by grade (Hint: A B-Tree Map cannot sort directly by value; you must use the grade as the key and the name as the value, or use
iter().rev()). - Difficulty ⭐⭐⭐: Write a function
fn analyze_collections(data: &[i32])that takes an integer slice as input, counts the number of occurrences of each digit using VecDeque, HashMap, and BTreeMap, respectively, and compares the performance differences among the three approaches (usingstd::time::Instantto measure the time).