ExamplesBy LevelBy TopicLearning Paths
264 Intermediate

264: Conditional Stopping with take_while()

Functional Programming

Tutorial

The Problem

Many algorithms need to consume elements from a sorted or ordered stream until a condition fails: reading log lines until a timestamp exceeds a threshold, parsing tokens until a delimiter is found, or collecting numbers until a negative value appears. The take_while() adapter makes this declarative — it yields elements while a predicate holds and stops the moment it first fails, discarding that element and all subsequent ones.

🎯 Learning Outcomes

  • • Understand that take_while(pred) stops permanently at the first false — even if later elements would satisfy the predicate
  • • Distinguish take_while() from filter(): take_while is a prefix operation, not a global filter
  • • Use take_while() on infinite iterators to create bounded sequences
  • • Recognize the "first false terminates" semantics as essential for ordered data processing
  • Code Example

    #![allow(clippy::all)]
    //! 264. Conditional stopping with take_while()
    //!
    //! `take_while(pred)` yields elements until the predicate first returns false.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_take_while_basic() {
            let result: Vec<i32> = [1, 2, 3, 4, 5]
                .iter()
                .copied()
                .take_while(|&x| x < 4)
                .collect();
            assert_eq!(result, vec![1, 2, 3]);
        }
    
        #[test]
        fn test_take_while_none_match() {
            let result: Vec<i32> = [-1i32, 2, 3]
                .iter()
                .copied()
                .take_while(|&x| x > 0)
                .collect();
            assert!(result.is_empty());
        }
    
        #[test]
        fn test_take_while_stops_early() {
            let result: Vec<i32> = [1i32, 2, 5, 1, 2]
                .iter()
                .copied()
                .take_while(|&x| x < 3)
                .collect();
            assert_eq!(result, vec![1, 2]);
        }
    
        #[test]
        fn test_take_while_infinite() {
            let result: Vec<u32> = (0u32..).take_while(|&x| x < 5).collect();
            assert_eq!(result, vec![0, 1, 2, 3, 4]);
        }
    }

    Key Differences

  • Prefix semantics: Both Rust and OCaml's take_while are prefix operations — stopping permanently at the first false, unlike filter which examines all elements.
  • Infinite sources: Rust's take_while() is essential for consuming infinite iterators safely; OCaml's Seq.take_while serves the same purpose.
  • Standard library: take_while is built into Rust's Iterator trait; OCaml's standard List module lacks it (available in third-party libraries).
  • Complementary: take_while(pred) and skip_while(pred) are inverses — together they split an ordered sequence at the first failure point.
  • OCaml Approach

    OCaml provides List.take_while via third-party libraries (like Base or Core), or it can be implemented recursively:

    let rec take_while pred = function
      | [] -> []
      | x :: xs -> if pred x then x :: take_while pred xs else []
    

    Seq.take_while exists for lazy sequences and has the same first-false-terminates semantics.

    Full Source

    #![allow(clippy::all)]
    //! 264. Conditional stopping with take_while()
    //!
    //! `take_while(pred)` yields elements until the predicate first returns false.
    
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_take_while_basic() {
            let result: Vec<i32> = [1, 2, 3, 4, 5]
                .iter()
                .copied()
                .take_while(|&x| x < 4)
                .collect();
            assert_eq!(result, vec![1, 2, 3]);
        }
    
        #[test]
        fn test_take_while_none_match() {
            let result: Vec<i32> = [-1i32, 2, 3]
                .iter()
                .copied()
                .take_while(|&x| x > 0)
                .collect();
            assert!(result.is_empty());
        }
    
        #[test]
        fn test_take_while_stops_early() {
            let result: Vec<i32> = [1i32, 2, 5, 1, 2]
                .iter()
                .copied()
                .take_while(|&x| x < 3)
                .collect();
            assert_eq!(result, vec![1, 2]);
        }
    
        #[test]
        fn test_take_while_infinite() {
            let result: Vec<u32> = (0u32..).take_while(|&x| x < 5).collect();
            assert_eq!(result, vec![0, 1, 2, 3, 4]);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        #[test]
        fn test_take_while_basic() {
            let result: Vec<i32> = [1, 2, 3, 4, 5]
                .iter()
                .copied()
                .take_while(|&x| x < 4)
                .collect();
            assert_eq!(result, vec![1, 2, 3]);
        }
    
        #[test]
        fn test_take_while_none_match() {
            let result: Vec<i32> = [-1i32, 2, 3]
                .iter()
                .copied()
                .take_while(|&x| x > 0)
                .collect();
            assert!(result.is_empty());
        }
    
        #[test]
        fn test_take_while_stops_early() {
            let result: Vec<i32> = [1i32, 2, 5, 1, 2]
                .iter()
                .copied()
                .take_while(|&x| x < 3)
                .collect();
            assert_eq!(result, vec![1, 2]);
        }
    
        #[test]
        fn test_take_while_infinite() {
            let result: Vec<u32> = (0u32..).take_while(|&x| x < 5).collect();
            assert_eq!(result, vec![0, 1, 2, 3, 4]);
        }
    }

    Exercises

  • Parse a sequence of positive integers followed by a sentinel -1 using take_while() to collect only the positive prefix.
  • Use take_while() on an infinite Fibonacci iterator to collect all Fibonacci numbers below one million.
  • Implement a simple CSV field parser that reads characters take_while(|&c| c != ',') from a character iterator.
  • Open Source Repos