LIFO and FIFO data structures with various implementations
Vec serves as an excellent stack with push() and pop()VecDeque provides efficient double-ended queue operations// Vec as stack - efficient!
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
let top = stack.pop(); // Some(2)
// VecDeque as queue
use std::collections::VecDeque;
let mut queue = VecDeque::new();
queue.push_back(1);
queue.push_back(2);
let front = queue.pop_front(); // Some(1)
A complete stack implementation with additional features.
use std::fmt;
/// A generic stack implementation
#[derive(Clone)]
pub struct Stack<T> {
data: Vec<T>,
capacity: Option<usize>,
}
impl<T> Stack<T> {
/// Create a new empty stack
pub fn new() -> Self {
Self {
data: Vec::new(),
capacity: None,
}
}
/// Create a stack with fixed capacity
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
capacity: Some(capacity),
}
}
/// Push an element onto the stack
pub fn push(&mut self, item: T) -> Result<(), StackError> {
if let Some(cap) = self.capacity {
if self.data.len() >= cap {
return Err(StackError::Overflow);
}
}
self.data.push(item);
Ok(())
}
/// Pop an element from the stack
pub fn pop(&mut self) -> Option<T> {
self.data.pop()
}
/// Peek at the top element without removing
pub fn peek(&self) -> Option<&T> {
self.data.last()
}
/// Peek at the top element mutably
pub fn peek_mut(&mut self) -> Option<&mut T> {
self.data.last_mut()
}
/// Check if stack is empty
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// Get the number of elements
pub fn len(&self) -> usize {
self.data.len()
}
/// Check if stack is full (only for bounded stacks)
pub fn is_full(&self) -> bool {
self.capacity.map_or(false, |cap| self.data.len() >= cap)
}
/// Clear all elements
pub fn clear(&mut self) {
self.data.clear();
}
/// Get remaining capacity
pub fn remaining_capacity(&self) -> Option<usize> {
self.capacity.map(|cap| cap - self.data.len())
}
/// Iterate over elements from top to bottom
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter().rev()
}
/// Drain all elements from top to bottom
pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
self.data.drain(..).rev()
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Debug> fmt::Debug for Stack<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Stack[")?;
for (i, item) in self.data.iter().rev().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}", item)?;
}
write!(f, "]")
}
}
impl<T> FromIterator<T> for Stack<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self {
data: iter.into_iter().collect(),
capacity: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StackError {
Overflow,
Underflow,
}
impl fmt::Display for StackError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StackError::Overflow => write!(f, "Stack overflow"),
StackError::Underflow => write!(f, "Stack underflow"),
}
}
}
impl std::error::Error for StackError {}
// Expression evaluation using stack
#[derive(Debug, Clone)]
pub enum Token {
Number(f64),
Operator(char),
LeftParen,
RightParen,
}
/// Evaluate postfix (Reverse Polish Notation) expression
pub fn eval_postfix(tokens: &[Token]) -> Result<f64, String> {
let mut stack = Stack::new();
for token in tokens {
match token {
Token::Number(n) => {
stack.push(*n).map_err(|_| "Stack overflow")?;
}
Token::Operator(op) => {
let b = stack.pop().ok_or("Missing operand")?;
let a = stack.pop().ok_or("Missing operand")?;
let result = match op {
'+' => a + b,
'-' => a - b,
'*' => a * b,
'/' => {
if b == 0.0 {
return Err("Division by zero".to_string());
}
a / b
}
'^' => a.powf(b),
_ => return Err(format!("Unknown operator: {}", op)),
};
stack.push(result).map_err(|_| "Stack overflow")?;
}
_ => return Err("Unexpected token in postfix expression".to_string()),
}
}
if stack.len() != 1 {
return Err("Invalid expression".to_string());
}
stack.pop().ok_or("Empty result".to_string())
}
/// Convert infix to postfix (Shunting Yard algorithm)
pub fn infix_to_postfix(tokens: &[Token]) -> Result<Vec<Token>, String> {
let mut output = Vec::new();
let mut operators = Stack::new();
let precedence = |op: char| -> i32 {
match op {
'+' | '-' => 1,
'*' | '/' => 2,
'^' => 3,
_ => 0,
}
};
let is_left_assoc = |op: char| -> bool {
op != '^'
};
for token in tokens {
match token {
Token::Number(_) => output.push(token.clone()),
Token::Operator(op) => {
while let Some(Token::Operator(top)) = operators.peek() {
if (is_left_assoc(*op) && precedence(*op) <= precedence(*top))
|| (!is_left_assoc(*op) && precedence(*op) < precedence(*top))
{
output.push(operators.pop().unwrap());
} else {
break;
}
}
operators.push(token.clone()).map_err(|_| "Stack overflow")?;
}
Token::LeftParen => {
operators.push(token.clone()).map_err(|_| "Stack overflow")?;
}
Token::RightParen => {
while let Some(top) = operators.peek() {
if matches!(top, Token::LeftParen) {
break;
}
output.push(operators.pop().unwrap());
}
if operators.pop().is_none() {
return Err("Mismatched parentheses".to_string());
}
}
}
}
while let Some(token) = operators.pop() {
if matches!(token, Token::LeftParen | Token::RightParen) {
return Err("Mismatched parentheses".to_string());
}
output.push(token);
}
Ok(output)
}
fn main() {
// Basic stack operations
let mut stack: Stack<i32> = Stack::new();
stack.push(1).unwrap();
stack.push(2).unwrap();
stack.push(3).unwrap();
println!("Stack: {:?}", stack);
println!("Top: {:?}", stack.peek());
println!("Pop: {:?}", stack.pop());
println!("After pop: {:?}", stack);
// Bounded stack
let mut bounded: Stack<i32> = Stack::with_capacity(2);
bounded.push(1).unwrap();
bounded.push(2).unwrap();
println!("\nBounded stack full: {}", bounded.is_full());
println!("Push to full: {:?}", bounded.push(3));
// Expression evaluation: (3 + 4) * 2 = 14
let infix = vec![
Token::LeftParen,
Token::Number(3.0),
Token::Operator('+'),
Token::Number(4.0),
Token::RightParen,
Token::Operator('*'),
Token::Number(2.0),
];
println!("\nInfix expression: (3 + 4) * 2");
let postfix = infix_to_postfix(&infix).unwrap();
println!("Postfix: {:?}", postfix);
let result = eval_postfix(&postfix).unwrap();
println!("Result: {}", result);
}
Multiple queue types for different use cases.
use std::collections::VecDeque;
use std::fmt;
/// Standard FIFO queue
#[derive(Clone)]
pub struct Queue<T> {
data: VecDeque<T>,
capacity: Option<usize>,
}
impl<T> Queue<T> {
pub fn new() -> Self {
Self {
data: VecDeque::new(),
capacity: None,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: VecDeque::with_capacity(capacity),
capacity: Some(capacity),
}
}
/// Add element to back of queue
pub fn enqueue(&mut self, item: T) -> Result<(), QueueError> {
if let Some(cap) = self.capacity {
if self.data.len() >= cap {
return Err(QueueError::Full);
}
}
self.data.push_back(item);
Ok(())
}
/// Remove element from front of queue
pub fn dequeue(&mut self) -> Option<T> {
self.data.pop_front()
}
/// Peek at front element
pub fn peek(&self) -> Option<&T> {
self.data.front()
}
/// Peek at back element
pub fn peek_back(&self) -> Option<&T> {
self.data.back()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_full(&self) -> bool {
self.capacity.map_or(false, |cap| self.data.len() >= cap)
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.data.iter()
}
}
impl<T> Default for Queue<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Debug> fmt::Debug for Queue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Queue{:?}", self.data)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QueueError {
Full,
Empty,
}
/// Circular buffer queue (ring buffer)
pub struct CircularQueue<T> {
data: Vec<Option<T>>,
head: usize,
tail: usize,
len: usize,
capacity: usize,
}
impl<T> CircularQueue<T> {
pub fn new(capacity: usize) -> Self {
let mut data = Vec::with_capacity(capacity);
for _ in 0..capacity {
data.push(None);
}
Self {
data,
head: 0,
tail: 0,
len: 0,
capacity,
}
}
pub fn enqueue(&mut self, item: T) -> Result<(), QueueError> {
if self.is_full() {
return Err(QueueError::Full);
}
self.data[self.tail] = Some(item);
self.tail = (self.tail + 1) % self.capacity;
self.len += 1;
Ok(())
}
pub fn dequeue(&mut self) -> Option<T> {
if self.is_empty() {
return None;
}
let item = self.data[self.head].take();
self.head = (self.head + 1) % self.capacity;
self.len -= 1;
item
}
pub fn peek(&self) -> Option<&T> {
if self.is_empty() {
None
} else {
self.data[self.head].as_ref()
}
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn is_full(&self) -> bool {
self.len == self.capacity
}
pub fn len(&self) -> usize {
self.len
}
pub fn capacity(&self) -> usize {
self.capacity
}
}
/// Priority queue (min-heap by default)
pub struct PriorityQueue<T: Ord> {
data: Vec<T>,
comparator: fn(&T, &T) -> std::cmp::Ordering,
}
impl<T: Ord> PriorityQueue<T> {
/// Create a min-heap (smallest element has highest priority)
pub fn new_min_heap() -> Self {
Self {
data: Vec::new(),
comparator: |a, b| a.cmp(b),
}
}
/// Create a max-heap (largest element has highest priority)
pub fn new_max_heap() -> Self {
Self {
data: Vec::new(),
comparator: |a, b| b.cmp(a),
}
}
/// Push element maintaining heap property
pub fn push(&mut self, item: T) {
self.data.push(item);
self.sift_up(self.data.len() - 1);
}
/// Pop highest priority element
pub fn pop(&mut self) -> Option<T> {
if self.data.is_empty() {
return None;
}
let last = self.data.len() - 1;
self.data.swap(0, last);
let item = self.data.pop();
if !self.data.is_empty() {
self.sift_down(0);
}
item
}
/// Peek at highest priority element
pub fn peek(&self) -> Option<&T> {
self.data.first()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
fn sift_up(&mut self, mut idx: usize) {
while idx > 0 {
let parent = (idx - 1) / 2;
if (self.comparator)(&self.data[idx], &self.data[parent])
== std::cmp::Ordering::Less
{
self.data.swap(idx, parent);
idx = parent;
} else {
break;
}
}
}
fn sift_down(&mut self, mut idx: usize) {
loop {
let left = 2 * idx + 1;
let right = 2 * idx + 2;
let mut smallest = idx;
if left < self.data.len()
&& (self.comparator)(&self.data[left], &self.data[smallest])
== std::cmp::Ordering::Less
{
smallest = left;
}
if right < self.data.len()
&& (self.comparator)(&self.data[right], &self.data[smallest])
== std::cmp::Ordering::Less
{
smallest = right;
}
if smallest != idx {
self.data.swap(idx, smallest);
idx = smallest;
} else {
break;
}
}
}
}
impl<T: Ord> Default for PriorityQueue<T> {
fn default() -> Self {
Self::new_min_heap()
}
}
/// Double-ended queue (Deque)
pub struct Deque<T> {
data: VecDeque<T>,
}
impl<T> Deque<T> {
pub fn new() -> Self {
Self {
data: VecDeque::new(),
}
}
pub fn push_front(&mut self, item: T) {
self.data.push_front(item);
}
pub fn push_back(&mut self, item: T) {
self.data.push_back(item);
}
pub fn pop_front(&mut self) -> Option<T> {
self.data.pop_front()
}
pub fn pop_back(&mut self) -> Option<T> {
self.data.pop_back()
}
pub fn peek_front(&self) -> Option<&T> {
self.data.front()
}
pub fn peek_back(&self) -> Option<&T> {
self.data.back()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
}
impl<T> Default for Deque<T> {
fn default() -> Self {
Self::new()
}
}
fn main() {
// Standard queue
println!("=== Standard Queue ===");
let mut queue: Queue<&str> = Queue::new();
queue.enqueue("first").unwrap();
queue.enqueue("second").unwrap();
queue.enqueue("third").unwrap();
println!("Queue: {:?}", queue);
println!("Dequeue: {:?}", queue.dequeue());
println!("After dequeue: {:?}", queue);
// Circular buffer
println!("\n=== Circular Buffer ===");
let mut circular = CircularQueue::new(3);
circular.enqueue(1).unwrap();
circular.enqueue(2).unwrap();
circular.enqueue(3).unwrap();
println!("Full: {}, Push result: {:?}", circular.is_full(), circular.enqueue(4));
circular.dequeue();
circular.enqueue(4).unwrap(); // Wraps around
println!("After wrap-around, front: {:?}", circular.peek());
// Priority queue
println!("\n=== Priority Queue (Min-Heap) ===");
let mut pq = PriorityQueue::new_min_heap();
pq.push(5);
pq.push(2);
pq.push(8);
pq.push(1);
pq.push(3);
print!("Priority order: ");
while let Some(item) = pq.pop() {
print!("{} ", item);
}
println!();
// Max-heap
println!("\n=== Priority Queue (Max-Heap) ===");
let mut max_pq = PriorityQueue::new_max_heap();
max_pq.push(5);
max_pq.push(2);
max_pq.push(8);
max_pq.push(1);
print!("Priority order: ");
while let Some(item) = max_pq.pop() {
print!("{} ", item);
}
println!();
// Deque
println!("\n=== Deque ===");
let mut deque = Deque::new();
deque.push_back(2);
deque.push_front(1);
deque.push_back(3);
println!("Front: {:?}, Back: {:?}", deque.peek_front(), deque.peek_back());
println!("Pop front: {:?}", deque.pop_front());
println!("Pop back: {:?}", deque.pop_back());
}
A practical task scheduler using queues.
use std::collections::VecDeque;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct Task {
pub id: u64,
pub name: String,
pub priority: Priority,
pub created_at: Instant,
pub estimated_duration: Duration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {
Low = 0,
Normal = 1,
High = 2,
Critical = 3,
}
impl Task {
pub fn new(id: u64, name: &str, priority: Priority, estimated_duration: Duration) -> Self {
Self {
id,
name: name.to_string(),
priority,
created_at: Instant::now(),
estimated_duration,
}
}
}
/// Multi-level feedback queue scheduler
pub struct TaskScheduler {
/// Queue for each priority level
queues: [VecDeque<Task>; 4],
/// Currently executing task
current: Option<Task>,
/// Completed tasks
completed: Vec<(Task, Duration)>,
/// Total tasks processed
total_processed: u64,
}
impl TaskScheduler {
pub fn new() -> Self {
Self {
queues: Default::default(),
current: None,
completed: Vec::new(),
total_processed: 0,
}
}
/// Submit a new task
pub fn submit(&mut self, task: Task) {
let queue_idx = task.priority as usize;
self.queues[queue_idx].push_back(task);
}
/// Get next task based on priority
pub fn next_task(&mut self) -> Option<Task> {
// Check queues from highest to lowest priority
for queue in self.queues.iter_mut().rev() {
if let Some(task) = queue.pop_front() {
return Some(task);
}
}
None
}
/// Start executing next task
pub fn start_next(&mut self) -> Option<&Task> {
if self.current.is_some() {
return self.current.as_ref();
}
self.current = self.next_task();
self.current.as_ref()
}
/// Complete current task
pub fn complete_current(&mut self, actual_duration: Duration) -> Option<Task> {
if let Some(task) = self.current.take() {
self.completed.push((task.clone(), actual_duration));
self.total_processed += 1;
Some(task)
} else {
None
}
}
/// Get queue lengths by priority
pub fn queue_lengths(&self) -> [usize; 4] {
[
self.queues[0].len(),
self.queues[1].len(),
self.queues[2].len(),
self.queues[3].len(),
]
}
/// Total pending tasks
pub fn pending_count(&self) -> usize {
self.queues.iter().map(|q| q.len()).sum()
}
/// Get statistics
pub fn stats(&self) -> SchedulerStats {
let total_wait_time: Duration = self
.completed
.iter()
.map(|(task, _)| task.created_at.elapsed())
.sum();
let avg_wait_time = if self.completed.is_empty() {
Duration::ZERO
} else {
total_wait_time / self.completed.len() as u32
};
SchedulerStats {
total_processed: self.total_processed,
pending: self.pending_count(),
completed: self.completed.len(),
avg_wait_time,
}
}
}
impl Default for TaskScheduler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct SchedulerStats {
pub total_processed: u64,
pub pending: usize,
pub completed: usize,
pub avg_wait_time: Duration,
}
/// Work-stealing queue for parallel processing
pub struct WorkStealingQueue<T> {
local: VecDeque<T>,
id: usize,
}
impl<T> WorkStealingQueue<T> {
pub fn new(id: usize) -> Self {
Self {
local: VecDeque::new(),
id,
}
}
/// Push to local queue
pub fn push(&mut self, item: T) {
self.local.push_back(item);
}
/// Pop from local queue (LIFO for locality)
pub fn pop(&mut self) -> Option<T> {
self.local.pop_back()
}
/// Steal from another queue (FIFO)
pub fn steal_from(&mut self, other: &mut WorkStealingQueue<T>) -> Option<T> {
other.local.pop_front()
}
/// Steal half of another queue's work
pub fn steal_half_from(&mut self, other: &mut WorkStealingQueue<T>) -> usize {
let steal_count = other.local.len() / 2;
for _ in 0..steal_count {
if let Some(item) = other.local.pop_front() {
self.local.push_back(item);
}
}
steal_count
}
pub fn len(&self) -> usize {
self.local.len()
}
pub fn is_empty(&self) -> bool {
self.local.is_empty()
}
pub fn id(&self) -> usize {
self.id
}
}
fn main() {
// Task scheduler demo
println!("=== Task Scheduler Demo ===\n");
let mut scheduler = TaskScheduler::new();
// Submit tasks with different priorities
scheduler.submit(Task::new(1, "Background sync", Priority::Low, Duration::from_secs(10)));
scheduler.submit(Task::new(2, "User request", Priority::Normal, Duration::from_secs(2)));
scheduler.submit(Task::new(3, "Database backup", Priority::Normal, Duration::from_secs(30)));
scheduler.submit(Task::new(4, "Emergency alert", Priority::Critical, Duration::from_secs(1)));
scheduler.submit(Task::new(5, "Cache refresh", Priority::High, Duration::from_secs(5)));
println!("Queue lengths [Low, Normal, High, Critical]: {:?}", scheduler.queue_lengths());
// Process tasks
println!("\nProcessing tasks in priority order:");
while let Some(task) = scheduler.start_next() {
println!(" Executing: {} (Priority: {:?})", task.name, task.priority);
// Simulate execution
let actual = task.estimated_duration;
scheduler.complete_current(actual);
}
let stats = scheduler.stats();
println!("\nScheduler Stats:");
println!(" Total processed: {}", stats.total_processed);
println!(" Completed: {}", stats.completed);
// Work-stealing demo
println!("\n=== Work-Stealing Queue Demo ===\n");
let mut worker1 = WorkStealingQueue::new(1);
let mut worker2 = WorkStealingQueue::new(2);
// Worker 1 has lots of work
for i in 0..10 {
worker1.push(format!("Task {}", i));
}
println!("Before stealing:");
println!(" Worker 1 queue length: {}", worker1.len());
println!(" Worker 2 queue length: {}", worker2.len());
// Worker 2 steals half
let stolen = worker2.steal_half_from(&mut worker1);
println!("\nWorker 2 stole {} tasks", stolen);
println!("\nAfter stealing:");
println!(" Worker 1 queue length: {}", worker1.len());
println!(" Worker 2 queue length: {}", worker2.len());
// Process stolen work
println!("\nWorker 2 processing stolen work:");
while let Some(task) = worker2.pop() {
println!(" {}", task);
}
}
| Implementation | Push | Pop | Peek | Memory | Use Case |
|---------------|------|-----|------|--------|----------|
| Vec (stack) | O(1)* | O(1) | O(1) | Contiguous | General stack |
| VecDeque (queue) | O(1)* | O(1) | O(1) | Ring buffer | General queue |
| Linked list | O(1) | O(1) | O(1) | Scattered | Stable pointers |
| Circular buffer | O(1) | O(1) | O(1) | Fixed | Bounded queues |
| Binary heap | O(log n) | O(log n) | O(1) | Array | Priority queue |
*amortized
// DON'T: Use Vec as queue (O(n) pop front)
let mut bad_queue = Vec::new();
bad_queue.push(1);
bad_queue.remove(0); // O(n)! Use VecDeque instead
// DON'T: Implement stack with linked list when Vec works
struct OvercomplicatedStack<T> {
head: Option<Box<Node<T>>>, // Unnecessary complexity
}
// DO: Just use Vec
let mut stack = Vec::new();
stack.push(1);
stack.pop();
// DON'T: Unbounded queues in production
fn process_requests(queue: &mut Queue<Request>) {
// Memory can grow forever!
}
// DO: Use bounded queues or apply backpressure
fn process_requests(queue: &mut Queue<Request>) -> Result<(), QueueError> {
if queue.len() > MAX_QUEUE_SIZE {
return Err(QueueError::Full); // Apply backpressure
}
// ...
}
| Operation | Vec Stack | VecDeque Queue | Priority Queue |
|-----------|-----------|----------------|----------------|
| Push | O(1) | O(1) | O(log n) |
| Pop | O(1) | O(1) | O(log n) |
| Peek | O(1) | O(1) | O(1) |
| Search | O(n) | O(n) | O(n) |
| Memory | Dense | Dense | Dense |
Run this code in the official Rust Playground