ExamplesBy LevelBy TopicLearning Paths
267 Intermediate

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

  • • Understand that cycle() repeats a finite iterator infinitely by cloning it at exhaustion
  • • Combine cycle() with take(n) to consume exactly n elements from the cycled source
  • • Use cycle() with zip() to assign repeating labels or colors to a longer sequence
  • • Recognize that cycle() requires the source iterator to implement Clone
  • Code 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

  • Clone requirement: Rust's cycle() requires Clone on the iterator — slices' iterators are Clone, but consuming iterators typically are not.
  • Infinite by design: The result iterator never terminates; always pair with take(), zip(), or take_while() to bound it.
  • Lazy evaluation: Both Rust's cycle() and OCaml's lazy sequence approach avoid materializing the repeated sequence; only the original source is stored.
  • Real-world uses: CSS :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

  • Implement a round-robin scheduler that distributes tasks among N workers using cycle() and zip().
  • Generate the alternating sequence [true, false, true, false, ...] for 20 elements using [true, false].iter().cycle().
  • Use cycle() to pad a shorter slice to match the length of a longer one by repeating the shorter slice's elements.
  • Open Source Repos