267: Infinite Cycling with cycle()
Functional Programming
Tutorial
The Problem
Repeating a pattern indefinitely is needed in round-robin scheduling, alternating colors in UI tables, repeating short sequences to fill longer ones, or generating periodic signals. The cycle() adapter creates an infinite iterator by cloning the source iterator each time it is exhausted. Combined with take() or zip(), it produces exactly as many repetitions as needed without manually computing modular indices.
🎯 Learning Outcomes
cycle() repeats a finite iterator infinitely by cloning it at exhaustioncycle() with take(n) to consume exactly n elements from the cycled sourcecycle() with zip() to assign repeating labels or colors to a longer sequencecycle() requires the source iterator to implement CloneCode Example
#![allow(clippy::all)]
//! 267. Infinite cycling with cycle()
//!
//! `cycle()` repeats a finite iterator infinitely. Requires `Clone` on the inner iterator.
#[cfg(test)]
mod tests {
#[test]
fn test_cycle_basic() {
let result: Vec<i32> = [1, 2, 3].iter().copied().cycle().take(7).collect();
assert_eq!(result, vec![1, 2, 3, 1, 2, 3, 1]);
}
#[test]
fn test_cycle_zip_round_robin() {
let items = [1i32, 2, 3, 4];
let labels = ["a", "b"];
let paired: Vec<_> = items.iter().zip(labels.iter().cycle()).collect();
assert_eq!(paired.len(), 4);
assert_eq!(*paired[2].1, "a");
}
#[test]
fn test_cycle_alternating() {
let alt: Vec<bool> = [true, false].iter().copied().cycle().take(6).collect();
assert_eq!(alt, vec![true, false, true, false, true, false]);
}
}Key Differences
cycle() requires Clone on the iterator — slices' iterators are Clone, but consuming iterators typically are not.take(), zip(), or take_while() to bound it.cycle() and OCaml's lazy sequence approach avoid materializing the repeated sequence; only the original source is stored.:nth-child patterns, round-robin load balancing, repeating background tiles, periodic waveform generation.OCaml Approach
OCaml's Seq module can model cycling with a recursive lazy function:
let rec cycle xs () = match Seq.uncons xs with
| None -> cycle xs () (* restart when exhausted *)
| Some (x, rest) -> Seq.Cons (x, cycle rest)
For lists, cycling is typically handled with modular index arithmetic: lst.(i mod List.length lst).
Full Source
#![allow(clippy::all)]
//! 267. Infinite cycling with cycle()
//!
//! `cycle()` repeats a finite iterator infinitely. Requires `Clone` on the inner iterator.
#[cfg(test)]
mod tests {
#[test]
fn test_cycle_basic() {
let result: Vec<i32> = [1, 2, 3].iter().copied().cycle().take(7).collect();
assert_eq!(result, vec![1, 2, 3, 1, 2, 3, 1]);
}
#[test]
fn test_cycle_zip_round_robin() {
let items = [1i32, 2, 3, 4];
let labels = ["a", "b"];
let paired: Vec<_> = items.iter().zip(labels.iter().cycle()).collect();
assert_eq!(paired.len(), 4);
assert_eq!(*paired[2].1, "a");
}
#[test]
fn test_cycle_alternating() {
let alt: Vec<bool> = [true, false].iter().copied().cycle().take(6).collect();
assert_eq!(alt, vec![true, false, true, false, true, false]);
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_cycle_basic() {
let result: Vec<i32> = [1, 2, 3].iter().copied().cycle().take(7).collect();
assert_eq!(result, vec![1, 2, 3, 1, 2, 3, 1]);
}
#[test]
fn test_cycle_zip_round_robin() {
let items = [1i32, 2, 3, 4];
let labels = ["a", "b"];
let paired: Vec<_> = items.iter().zip(labels.iter().cycle()).collect();
assert_eq!(paired.len(), 4);
assert_eq!(*paired[2].1, "a");
}
#[test]
fn test_cycle_alternating() {
let alt: Vec<bool> = [true, false].iter().copied().cycle().take(6).collect();
assert_eq!(alt, vec![true, false, true, false, true, false]);
}
}
Exercises
cycle() and zip().[true, false, true, false, ...] for 20 elements using [true, false].iter().cycle().cycle() to pad a shorter slice to match the length of a longer one by repeating the shorter slice's elements.