ExamplesBy LevelBy TopicLearning Paths
1032 Intermediate

1032-vec-deque-rotation — VecDeque Rotation

Functional Programming

Tutorial

The Problem

A double-ended queue (deque) supports efficient insertion and removal at both ends. Ring buffers — fixed-size deques used for circular logs, producer-consumer queues, and audio sample buffers — are a classic application. Rotation (shifting all elements left or right by k positions) requires O(1) with a proper deque but O(n) with a plain Vec.

Rust's VecDeque is a ring-buffer backed deque providing O(1) amortized push and pop at both ends. Its rotate_left and rotate_right methods are O(min(k, n-k)), making rotation efficient.

🎯 Learning Outcomes

  • • Use VecDeque for O(1) push and pop at both ends
  • • Apply rotate_left and rotate_right for efficient rotation
  • • Implement a sliding window using VecDeque as a fixed-size buffer
  • • Use VecDeque as a BFS queue
  • • Convert between Vec and VecDeque
  • Code Example

    #![allow(clippy::all)]
    // 1032: VecDeque Rotation — Efficient Front/Back Operations
    // VecDeque is a ring buffer: O(1) push/pop at both ends
    
    use std::collections::VecDeque;
    
    /// Basic front/back operations
    fn basic_deque() {
        let mut dq = VecDeque::new();
        dq.push_back(1);
        dq.push_back(2);
        dq.push_back(3);
        dq.push_front(0);
    
        assert_eq!(dq.pop_front(), Some(0));
        assert_eq!(dq.pop_front(), Some(1));
        assert_eq!(dq.pop_back(), Some(3));
        assert_eq!(dq.pop_back(), Some(2));
        assert!(dq.is_empty());
    }
    
    /// Rotation using VecDeque::rotate_left/rotate_right
    fn rotation() {
        let mut dq: VecDeque<_> = [1, 2, 3, 4, 5].into_iter().collect();
    
        // Rotate left by 2: [3, 4, 5, 1, 2]
        dq.rotate_left(2);
        assert_eq!(dq, [3, 4, 5, 1, 2].into_iter().collect::<VecDeque<_>>());
    
        // Rotate right by 2: back to [1, 2, 3, 4, 5]
        dq.rotate_right(2);
        assert_eq!(dq, [1, 2, 3, 4, 5].into_iter().collect::<VecDeque<_>>());
    }
    
    /// Using VecDeque as a sliding window
    fn sliding_window() {
        let data = vec![1, 2, 3, 4, 5, 6, 7];
        let window_size = 3;
        let mut window: VecDeque<i32> = VecDeque::with_capacity(window_size);
        let mut sums = Vec::new();
    
        for &val in &data {
            window.push_back(val);
            if window.len() > window_size {
                window.pop_front();
            }
            if window.len() == window_size {
                let sum: i32 = window.iter().sum();
                sums.push(sum);
            }
        }
    
        assert_eq!(sums, vec![6, 9, 12, 15, 18]); // 1+2+3, 2+3+4, ...
    }
    
    /// VecDeque to Vec and back
    fn conversions() {
        let v = vec![1, 2, 3, 4, 5];
        let mut dq: VecDeque<_> = v.into_iter().collect();
    
        dq.push_front(0);
        // make_contiguous ensures internal buffer is contiguous
        dq.make_contiguous();
    
        let v: Vec<_> = dq.into_iter().collect();
        assert_eq!(v, vec![0, 1, 2, 3, 4, 5]);
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_basic_deque() {
            basic_deque();
        }
    
        #[test]
        fn test_rotation() {
            rotation();
        }
    
        #[test]
        fn test_sliding_window() {
            sliding_window();
        }
    
        #[test]
        fn test_conversions() {
            conversions();
        }
    
        #[test]
        fn test_indexed_access() {
            let dq: VecDeque<_> = [10, 20, 30].into_iter().collect();
            assert_eq!(dq[0], 10);
            assert_eq!(dq[1], 20);
            assert_eq!(dq[2], 30);
        }
    }

    Key Differences

  • Rotation: Rust's VecDeque::rotate_left is a built-in method; OCaml requires manual element movement or a custom implementation.
  • Ring buffer: VecDeque is backed by a ring buffer with a start offset; OCaml's Queue uses a linked list.
  • Index access: VecDeque supports O(1) index access via deque[i]; OCaml's Queue is sequential-access only.
  • Conversion: Rust provides VecDeque::from(vec) and vec.into() for zero-copy conversion; OCaml requires explicit iteration.
  • OCaml Approach

    OCaml's standard library lacks a deque, but Queue provides a FIFO queue with O(1) add and take:

    let sliding_window data window_size =
      let q = Queue.create () in
      List.map (fun x ->
        Queue.add x q;
        if Queue.length q > window_size then ignore (Queue.pop q);
        Queue.fold (+) 0 q  (* sum of window *)
      ) data
    

    The Base.Deque module and containers library provide full double-ended queues with rotation.

    Full Source

    #![allow(clippy::all)]
    // 1032: VecDeque Rotation — Efficient Front/Back Operations
    // VecDeque is a ring buffer: O(1) push/pop at both ends
    
    use std::collections::VecDeque;
    
    /// Basic front/back operations
    fn basic_deque() {
        let mut dq = VecDeque::new();
        dq.push_back(1);
        dq.push_back(2);
        dq.push_back(3);
        dq.push_front(0);
    
        assert_eq!(dq.pop_front(), Some(0));
        assert_eq!(dq.pop_front(), Some(1));
        assert_eq!(dq.pop_back(), Some(3));
        assert_eq!(dq.pop_back(), Some(2));
        assert!(dq.is_empty());
    }
    
    /// Rotation using VecDeque::rotate_left/rotate_right
    fn rotation() {
        let mut dq: VecDeque<_> = [1, 2, 3, 4, 5].into_iter().collect();
    
        // Rotate left by 2: [3, 4, 5, 1, 2]
        dq.rotate_left(2);
        assert_eq!(dq, [3, 4, 5, 1, 2].into_iter().collect::<VecDeque<_>>());
    
        // Rotate right by 2: back to [1, 2, 3, 4, 5]
        dq.rotate_right(2);
        assert_eq!(dq, [1, 2, 3, 4, 5].into_iter().collect::<VecDeque<_>>());
    }
    
    /// Using VecDeque as a sliding window
    fn sliding_window() {
        let data = vec![1, 2, 3, 4, 5, 6, 7];
        let window_size = 3;
        let mut window: VecDeque<i32> = VecDeque::with_capacity(window_size);
        let mut sums = Vec::new();
    
        for &val in &data {
            window.push_back(val);
            if window.len() > window_size {
                window.pop_front();
            }
            if window.len() == window_size {
                let sum: i32 = window.iter().sum();
                sums.push(sum);
            }
        }
    
        assert_eq!(sums, vec![6, 9, 12, 15, 18]); // 1+2+3, 2+3+4, ...
    }
    
    /// VecDeque to Vec and back
    fn conversions() {
        let v = vec![1, 2, 3, 4, 5];
        let mut dq: VecDeque<_> = v.into_iter().collect();
    
        dq.push_front(0);
        // make_contiguous ensures internal buffer is contiguous
        dq.make_contiguous();
    
        let v: Vec<_> = dq.into_iter().collect();
        assert_eq!(v, vec![0, 1, 2, 3, 4, 5]);
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_basic_deque() {
            basic_deque();
        }
    
        #[test]
        fn test_rotation() {
            rotation();
        }
    
        #[test]
        fn test_sliding_window() {
            sliding_window();
        }
    
        #[test]
        fn test_conversions() {
            conversions();
        }
    
        #[test]
        fn test_indexed_access() {
            let dq: VecDeque<_> = [10, 20, 30].into_iter().collect();
            assert_eq!(dq[0], 10);
            assert_eq!(dq[1], 20);
            assert_eq!(dq[2], 30);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_basic_deque() {
            basic_deque();
        }
    
        #[test]
        fn test_rotation() {
            rotation();
        }
    
        #[test]
        fn test_sliding_window() {
            sliding_window();
        }
    
        #[test]
        fn test_conversions() {
            conversions();
        }
    
        #[test]
        fn test_indexed_access() {
            let dq: VecDeque<_> = [10, 20, 30].into_iter().collect();
            assert_eq!(dq[0], 10);
            assert_eq!(dq[1], 20);
            assert_eq!(dq[2], 30);
        }
    }

    Deep Comparison

    VecDeque Rotation — Comparison

    Core Insight

    Rust provides a purpose-built ring buffer (VecDeque) with O(1) operations at both ends and built-in rotation. OCaml's standard library lacks this; the common workaround is a two-list queue (amortized O(1)) or a simple list (O(1) front, O(n) back).

    OCaml Approach

  • • List: O(1) push/pop front, O(n) back operations
  • • Two-list deque: { front; back } with lazy reversal for amortized O(1)
  • • Manual rotation: split list at index, append halves
  • Queue module exists but is mutable and not a deque
  • Rust Approach

  • VecDeque<T>: ring buffer backed by contiguous array
  • • O(1) push_front, push_back, pop_front, pop_back
  • rotate_left(n) / rotate_right(n): built-in rotation
  • • Indexed access via dq[i]
  • make_contiguous() to linearize internal buffer
  • • Excellent for sliding windows
  • Comparison Table

    FeatureOCaml (list/two-list)Rust (VecDeque)
    Push frontO(1)O(1)
    Push backO(n) list / amortized O(1)O(1)
    Pop frontO(1)O(1)
    Pop backO(n) list / amortized O(1)O(1)
    RotationManual O(n)Built-in O(n)
    Random accessO(n)O(1)
    Memory layoutLinked nodesContiguous ring buffer
    Cache friendlyNoYes

    Exercises

  • Implement a RateLimiter that tracks requests using a VecDeque<Instant>, sliding the window to count requests in the last N seconds.
  • Write rotate_string(s: &str, k: usize) -> String using VecDeque<char> to rotate a string's characters.
  • Implement a fixed-capacity circular log buffer CircularLog<T> backed by VecDeque<T> that keeps only the last N entries.
  • Open Source Repos