Standard Collections
VecDeque<T>
VecDeque<T> (std::collections::VecDeque<T>) is a growable double-ended queue implemented as a circular ring buffer on the heap, supporting amortized $O(1)$ insertion and removal at both the front and the back, as well as $O(1)$ indexing.
Ring Buffer Memory Layout
Unlike Vec<T>, whose logical element 0 always sits at physical offset 0 of its heap allocation (making vec.insert(0, x) an $O(n)$ shift), VecDeque<T> tracks a head index and len that wrap around the end of the allocated buffer:
Physical Heap Buffer (capacity = 6, head = 4, len = 4):
+------+------+------+------+------+------+
| C | D | -- | -- | A | B |
+------+------+------+------+------+------+
[0] [1] [2] [3] [4] [5]
▲ ▲
tail head (logical index 0)
Logical View (q[0]..q[3]): [ A, B, C, D ]
Core Double-Ended Queue Operations
| Operation | Front (Index 0) |
Back (Index len - 1) |
Time Complexity |
|---|---|---|---|
| Insert element | q.push_front(x) |
q.push_back(x) |
Amortized $O(1)$ |
| Remove element | q.pop_front() -> Option<T> |
q.pop_back() -> Option<T> |
$O(1)$ |
| Inspect element | q.front() -> Option<&T> |
q.back() -> Option<&T> |
$O(1)$ |
| Mutate element | q.front_mut() -> Option<&mut T> |
q.back_mut() -> Option<&mut T> |
$O(1)$ |
| Index by offset | q[i] / q.get(i) |
— | $O(1)$ |
use std::collections::VecDeque;
fn main() {
let mut deque = VecDeque::from(["middle"]);
deque.push_front("first");
deque.push_back("last");
println!("Queue: {deque:?}");
println!("Front: {:?}, Back: {:?}", deque.front(), deque.back());
println!("Popped FIFO: {:?}", deque.pop_front());
println!("Remaining: {deque:?}");
}
Queue: ["first", "middle", "last"]
Front: Some(&"first"), Back: Some(&"last")
Popped FIFO: Some("first")
Remaining: ["middle", "last"]
Accessing Contiguous Slices (as_slices() and make_contiguous())
Because the ring buffer can wrap around the physical end of its allocation, a VecDeque<T> is not guaranteed to be a single contiguous slice &[T] (and therefore does not implement Deref<Target = [T]>):
q.as_slices() -> (&[T], &[T])returns the two contiguous halves(head_to_end, start_to_tail)in $O(1)$ time without moving any elements.q.make_contiguous() -> &mut [T]rotates the internal buffer in place so all elements sit in one contiguous slice&mut [T](allowing you to call slice methods like.sort()):
use std::collections::VecDeque;
fn main() {
let mut q = VecDeque::with_capacity(4);
q.push_back(30);
q.push_back(10);
q.push_front(20); // Wraps around physical buffer
let (first_half, second_half) = q.as_slices();
println!("Before make_contiguous: {first_half:?} and {second_half:?}");
let contiguous: &mut [i32] = q.make_contiguous();
contiguous.sort();
println!("Sorted contiguous slice: {contiguous:?}");
}
Before make_contiguous: [20] and [30, 10]
Sorted contiguous slice: [10, 20, 30]