Ordered Collections
pop_first() and pop_last()
The .pop_first() and .pop_last() methods on BTreeMap<K, V> and BTreeSet<T> remove and return the minimum or maximum element in $O(\log n)$ time, enabling BTreeMap and BTreeSet to act as double-ended priority queues with unique keys.
Signatures
// On BTreeMap<K, V>:
pub fn pop_first(&mut self) -> Option<(K, V)>;
pub fn pop_last(&mut self) -> Option<(K, V)>;
// On BTreeSet<T>:
pub fn pop_first(&mut self) -> Option<T>;
pub fn pop_last(&mut self) -> Option<T>;
| Method | Removes and Returns | Empty Collection Return |
|---|---|---|
map.pop_first() |
Some((min_key, val)) |
None |
map.pop_last() |
Some((max_key, val)) |
None |
set.pop_first() |
Some(min_elem) |
None |
set.pop_last() |
Some(max_elem) |
None |
Using BTreeSet as a Double-Ended Priority Queue (Min-Max Queue)
Unlike BinaryHeap<T>, which only supports popping from one end (.pop() removes the maximum) and allows duplicate entries, BTreeSet<T> supports popping both the minimum (.pop_first()) and the maximum (.pop_last()) in $O(\log n)$ time while automatically deduplicating elements:
use std::collections::BTreeSet;
fn main() {
let mut job_priorities = BTreeSet::from([10, 50, 25, 90, 40]);
// Pop the highest-priority job (largest):
println!("Highest priority popped: {:?}", job_priorities.pop_last());
// Pop the lowest-priority job (smallest):
println!("Lowest priority popped: {:?}", job_priorities.pop_first());
println!("Remaining jobs: {job_priorities:?}");
}
Highest priority popped: Some(90)
Lowest priority popped: Some(10)
Remaining jobs: {25, 40, 50}
Timer Wheel / Event Scheduler Loop with BTreeMap::pop_first()
Because BTreeMap::pop_first() always extracts the earliest timestamp key (timestamp, task), driving an event loop in chronological order is a simple while let Some((time, event)) = schedule.pop_first() loop:
use std::collections::BTreeMap;
fn main() {
let mut schedule = BTreeMap::from([
(300_u64, "send_summary"),
(100, "connect_db"),
(200, "run_migrations"),
]);
while let Some((timestamp_ms, action)) = schedule.pop_first() {
println!("@{timestamp_ms}ms -> executing '{action}'");
if action == "connect_db" {
// Dynamically schedule a follow-up task during execution:
schedule.insert(150, "verify_schema");
}
}
}
@100ms -> executing 'connect_db'
@150ms -> executing 'verify_schema'
@200ms -> executing 'run_migrations'
@300ms -> executing 'send_summary'