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
take_while(pred) stops permanently at the first false — even if later elements would satisfy the predicatetake_while() from filter(): take_while is a prefix operation, not a global filtertake_while() on infinite iterators to create bounded sequencesCode 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
take_while are prefix operations — stopping permanently at the first false, unlike filter which examines all elements.take_while() is essential for consuming infinite iterators safely; OCaml's Seq.take_while serves the same purpose.take_while is built into Rust's Iterator trait; OCaml's standard List module lacks it (available in third-party libraries).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
-1 using take_while() to collect only the positive prefix.take_while() on an infinite Fibonacci iterator to collect all Fibonacci numbers below one million.take_while(|&c| c != ',') from a character iterator.