๐Ÿฆ€ Functional Rust

019: Rotate Left

Difficulty: 1 Level: Foundations Rotate a list left by N positions โ€” elements that fall off the front wrap around to the back.

The Problem This Solves

Rotating a list is everywhere: circular buffers in embedded systems, round-robin scheduling, carousel UI components, shift-register simulations. Given `['a','b','c','d','e','f','g','h']` and `n=3`, the result is `['d','e','f','g','h','a','b','c']` โ€” the first three elements moved to the end. In Python you'd write `lst[n:] + lst[:n]` for a positive rotation, or use `collections.deque` for efficient in-place rotation. This is clean, but you still need to handle negative `n` and `n > len(lst)` manually. Rust makes both cases explicit and efficient. The modular arithmetic normalizes any `n` โ€” positive, negative, or larger than the list โ€” into a single canonical shift, then a slice operation does the rotation in two array copies with no extra logic.

The Intuition

In Python: `lst[n:] + lst[:n]` In JavaScript: `[...lst.slice(n), ...lst.slice(0, n)]` A left rotation by `n` is really just: "start reading at position `n`, wrap around when you reach the end." Rust's slice concatenation (`lst[shift..]` + `lst[..shift]`) does exactly that. The tricky part is normalizing `n`. If the list has 8 elements and you rotate by 11, that's the same as rotating by 3 (11 % 8 = 3). If you rotate by -2, that's the same as rotating left by 6 (or right by 2). The formula `((n % len) + len) % len` handles all cases in one line.

How It Works in Rust

fn rotate<T: Clone>(lst: &[T], n: i64) -> Vec<T> {
 if lst.is_empty() {
     return vec![];
 }
 let len = lst.len() as i64;
 // Normalize n: handles negative, zero, and n > len
 let shift = ((n % len) + len) as usize % lst.len();
 let mut result = lst[shift..].to_vec();   // elements from shift onward
 result.extend_from_slice(&lst[..shift]);  // wrap-around elements
 result
}
There's also a `rotate_cycle` version that uses `.cycle().skip(shift).take(len)` โ€” the iterator chain reads almost like an English sentence: "cycle the list endlessly, skip the first `shift` items, take exactly `len` items."

What This Unlocks

Key Differences

ConceptOCamlRust
RotationSplit + `@` (list concat)Slice concat: `extend_from_slice`
Negative rotationSeparate case or `mod` trickSame formula: `((n % len) + len) % len`
Cyclic iteration`List.init` with modular index`.cycle().skip(n).take(len)`
Signed index`int``i64` (explicit sign), then cast to `usize`
Empty list guardPattern match on `[]``.is_empty()` early return
// Rotate List Left โ€” 99 Problems #19
// Rotate a list left by n positions.
// rotate ['a','b','c','d','e','f','g','h'] 3
//   โ†’ ['d','e','f','g','h','a','b','c']
// Negative n rotates right.

fn rotate<T: Clone>(lst: &[T], n: i64) -> Vec<T> {
    if lst.is_empty() {
        return vec![];
    }
    let len = lst.len() as i64;
    // Normalize n to 0..len
    let shift = ((n % len) + len) as usize % lst.len();
    let mut result = lst[shift..].to_vec();
    result.extend_from_slice(&lst[..shift]);
    result
}

/// Using iterators with cycle.
fn rotate_cycle<T: Clone>(lst: &[T], n: i64) -> Vec<T> {
    if lst.is_empty() {
        return vec![];
    }
    let len = lst.len() as i64;
    let shift = (((n % len) + len) as usize) % lst.len();
    lst.iter()
        .cycle()
        .skip(shift)
        .take(lst.len())
        .cloned()
        .collect()
}

fn main() {
    let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
    println!("Input:        {:?}", input);
    println!("Rotate +3:    {:?}", rotate(&input, 3));
    println!("Rotate -2:    {:?}", rotate(&input, -2));
    println!("Rotate 0:     {:?}", rotate(&input, 0));
    println!("Rotate +11:   {:?}", rotate(&input, 11));
    println!("Cycle +3:     {:?}", rotate_cycle(&input, 3));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_rotate_positive() {
        let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
        assert_eq!(
            rotate(&input, 3),
            vec!['d', 'e', 'f', 'g', 'h', 'a', 'b', 'c']
        );
    }

    #[test]
    fn test_rotate_negative() {
        let input = vec!['a', 'b', 'c', 'd'];
        assert_eq!(rotate(&input, -1), vec!['d', 'a', 'b', 'c']);
    }

    #[test]
    fn test_rotate_zero() {
        let input = vec![1, 2, 3, 4];
        assert_eq!(rotate(&input, 0), input);
    }

    #[test]
    fn test_rotate_cycle_matches() {
        let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
        assert_eq!(rotate(&input, 3), rotate_cycle(&input, 3));
    }
}
(* Rotate Left *)
(* OCaml 99 Problems #19 *)

(* Implementation for example 19 *)

(* Tests *)
let () =
  (* Add tests *)
  print_endline "โœ“ OCaml tests passed"