Introduction to For Loops in Rust
The for loop in Rust is a powerful and expressive construct for iterating over collections, ranges, and any type that implements the Iterator trait. Unlike traditional C-style for loops with initialization, condition, and increment, Rust's for loop is designed to be safe, expressive, and work seamlessly with its ownership system.
Key Concepts
- Iterator-based: Works with any type that implements
Iterator - Ownership-aware: Respects borrowing rules
- Range syntax: Built-in support for numeric ranges
- Pattern matching: Can destructure items during iteration
- Zero-cost: Compiles to efficient loops without overhead
1. Basic For Loop Syntax
Simple Range Iteration
fn main() { // Iterate over a range (exclusive of upper bound) for i in 0..5 { println!("i = {}", i); // Prints 0, 1, 2, 3, 4 } // Iterate over inclusive range for i in 0..=5 { println!("i = {}", i); // Prints 0, 1, 2, 3, 4, 5 } // Reverse iteration for i in (0..5).rev() { println!("Reverse i = {}", i); // Prints 4, 3, 2, 1, 0 } // Step by (using step_by) for i in (0..=10).step_by(2) { println!("Even: {}", i); // Prints 0, 2, 4, 6, 8, 10 } } Iterating Over Collections
fn main() { // Array iteration (by reference) let arr = [1, 2, 3, 4, 5]; for element in &arr { println!("Element: {}", element); } // Vector iteration let vec = vec!["apple", "banana", "cherry"]; for fruit in &vec { println!("Fruit: {}", fruit); } // String iteration (by chars) let text = "hello"; for c in text.chars() { println!("Char: {}", c); } // String iteration (by bytes) for b in text.bytes() { println!("Byte: {}", b); } } For Loop with Ownership
fn main() { let vec = vec![1, 2, 3, 4, 5]; // Iterate by value (consumes the vector) for element in vec { println!("Element: {}", element); } // println!("{:?}", vec); // Error: vec moved // Iterate by reference (doesn't consume) let vec2 = vec![1, 2, 3]; for element in &vec2 { println!("Element: {}", element); } println!("Still have vec2: {:?}", vec2); // OK // Iterate by mutable reference (can modify) let mut vec3 = vec![1, 2, 3]; for element in &mut vec3 { *element *= 2; } println!("Modified vec3: {:?}", vec3); // [2, 4, 6] } 2. For Loop with Iterators
Using .iter(), .iter_mut(), .into_iter()
fn main() { let numbers = vec![1, 2, 3, 4, 5]; // .iter() - borrows each element for num in numbers.iter() { println!("Borrowed: {}", num); } println!("Still own numbers: {:?}", numbers); // .iter_mut() - mutably borrows each element let mut numbers = vec![1, 2, 3]; for num in numbers.iter_mut() { *num *= 2; } println!("After mutable iteration: {:?}", numbers); // .into_iter() - takes ownership let numbers = vec![1, 2, 3]; for num in numbers.into_iter() { println!("Owned: {}", num); } // println!("{:?}", numbers); // Error: numbers moved // The three forms are equivalent to: let numbers = vec![1, 2, 3]; // &numbers -> .iter() for num in &numbers { println!("{}", num); } // &mut numbers -> .iter_mut() let mut numbers = vec![1, 2, 3]; for num in &mut numbers { *num *= 2; } // numbers -> .into_iter() let numbers = vec![1, 2, 3]; for num in numbers { println!("{}", num); } } Custom Iterators
struct Counter { count: u32, max: u32, } impl Counter { fn new(max: u32) -> Self { Counter { count: 0, max } } } impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<Self::Item> { if self.count < self.max { self.count += 1; Some(self.count) } else { None } } } struct Fibonacci { current: u32, next: u32, } impl Fibonacci { fn new() -> Self { Fibonacci { current: 0, next: 1 } } } impl Iterator for Fibonacci { type Item = u32; fn next(&mut self) -> Option<Self::Item> { let new_next = self.current + self.next; self.current = self.next; self.next = new_next; Some(self.current) } } fn main() { // Using custom Counter iterator for i in Counter::new(5) { println!("Counter: {}", i); } // Using Fibonacci iterator (take first 10) for num in Fibonacci::new().take(10) { println!("Fibonacci: {}", num); } } 3. For Loop with Enumerate and Index
Getting Index with enumerate()
fn main() { let fruits = vec!["apple", "banana", "cherry", "date"]; // Get index and value for (index, fruit) in fruits.iter().enumerate() { println!("Fruit #{}: {}", index + 1, fruit); } // With mutable access let mut numbers = vec![10, 20, 30]; for (index, num) in numbers.iter_mut().enumerate() { *num += index as i32; } println!("After adding index: {:?}", numbers); // Using enumerate with range for (i, j) in (10..=20).step_by(2).enumerate() { println!("Index: {}, Value: {}", i, j); } // Enumerate with patterns let pairs = vec![(1, 2), (3, 4), (5, 6)]; for (index, (x, y)) in pairs.iter().enumerate() { println!("Pair {}: ({}, {})", index, x, y); } } Traditional Index-based Loops
fn main() { let arr = [1, 2, 3, 4, 5]; // Using range and indexing (less idiomatic) for i in 0..arr.len() { println!("arr[{}] = {}", i, arr[i]); } // With mutable access let mut vec = vec![1, 2, 3, 4, 5]; for i in 0..vec.len() { vec[i] *= 2; } println!("Doubled: {:?}", vec); // When you need to modify based on index let mut numbers = vec![1, 2, 3, 4, 5]; for i in 0..numbers.len() { numbers[i] += i as i32; } println!("Index added: {:?}", numbers); // Accessing multiple elements with index let arr = [1, 2, 3, 4, 5]; for i in 0..arr.len() - 1 { println!("{} + {} = {}", arr[i], arr[i + 1], arr[i] + arr[i + 1]); } } 4. For Loop with Pattern Matching
Destructuring in For Loops
fn main() { // Tuple destructuring let pairs = vec![(1, 2), (3, 4), (5, 6)]; for (x, y) in &pairs { println!("x = {}, y = {}", x, y); } // Struct destructuring struct Point { x: i32, y: i32, } let points = vec![ Point { x: 1, y: 2 }, Point { x: 3, y: 4 }, Point { x: 5, y: 6 }, ]; for Point { x, y } in &points { println!("Point at ({}, {})", x, y); } // Destructuring with renaming for Point { x: a, y: b } in &points { println!("Coordinates: ({}, {})", a, b); } // Nested destructuring let complex = vec![ (1, (2, 3)), (4, (5, 6)), (7, (8, 9)), ]; for (a, (b, c)) in &complex { println!("a = {}, b = {}, c = {}", a, b, c); } } Pattern Matching with Enums
enum Color { Red, Green, Blue, RGB(u8, u8, u8), Hex(String), } enum Option<T> { Some(T), None, } fn main() { let colors = vec![ Color::Red, Color::Green, Color::RGB(255, 0, 0), Color::Hex("#00FF00".to_string()), Color::Blue, ]; for color in &colors { match color { Color::Red => println!("It's red!"), Color::Green => println!("It's green!"), Color::Blue => println!("It's blue!"), Color::RGB(r, g, b) => println!("RGB: ({}, {}, {})", r, g, b), Color::Hex(h) => println!("Hex: {}", h), } } // Using if let in for loops let options = vec![ Some(1), None, Some(2), Some(3), None, ]; for opt in &options { if let Some(value) = opt { println!("Got value: {}", value); } else { println!("Got None"); } } } 5. For Loop Control Flow
Break and Continue
fn main() { // Break - exit loop early println!("Break example:"); for i in 0..10 { if i == 5 { break; } println!("i = {}", i); } // Continue - skip to next iteration println!("\nContinue example:"); for i in 0..10 { if i % 2 == 0 { continue; } println!("Odd: {}", i); } // Break with value (in loop expressions) let result = loop { break 42; // break returns value }; println!("Loop result: {}", result); // Break from nested loops (with labels) 'outer: for i in 0..5 { for j in 0..5 { if i == 2 && j == 2 { break 'outer; // breaks outer loop } println!("i = {}, j = {}", i, j); } } // Continue with label 'outer: for i in 0..3 { for j in 0..3 { if i == 1 { continue 'outer; // continues outer loop } println!("i = {}, j = {}", i, j); } } } Complex Control Flow
fn main() { // Early return from function inside loop fn find_first_even(numbers: &[i32]) -> Option<i32> { for &num in numbers { if num % 2 == 0 { return Some(num); } } None } let numbers = vec![1, 3, 5, 7, 8, 9]; match find_first_even(&numbers) { Some(n) => println!("First even: {}", n), None => println!("No evens found"), } // Break with condition let mut sum = 0; for i in 0.. { sum += i; if sum > 100 { println!("Sum exceeded 100 at i = {}", i); break; } } // Using while let inside for let mut stack = vec![1, 2, 3, 4, 5]; for _ in 0..3 { while let Some(top) = stack.pop() { println!("Popped: {}", top); if top == 3 { break; } } } } 6. For Loop with Collections
Working with Vectors
fn main() { let mut numbers = vec![1, 2, 3, 4, 5]; // Modify while iterating for num in &mut numbers { *num *= 2; } println!("Doubled: {:?}", numbers); // Filter and collect let evens: Vec<_> = numbers.iter() .filter(|&&x| x % 2 == 0) .collect(); println!("Evens: {:?}", evens); // Transform and collect let squares: Vec<_> = numbers.iter() .map(|&x| x * x) .collect(); println!("Squares: {:?}", squares); // Remove elements while iterating (use retain) numbers.retain(|&x| x > 5); println!("After retain: {:?}", numbers); // For loop with draining let mut numbers = vec![1, 2, 3, 4, 5]; for num in numbers.drain(..2) { println!("Drained: {}", num); } println!("Remaining: {:?}", numbers); } Working with HashMaps
use std::collections::HashMap; fn main() { let mut scores = HashMap::new(); scores.insert("Alice", 100); scores.insert("Bob", 80); scores.insert("Charlie", 95); // Iterate over key-value pairs for (name, score) in &scores { println!("{}: {}", name, score); } // Iterate over keys for name in scores.keys() { println!("Name: {}", name); } // Iterate over values for score in scores.values() { println!("Score: {}", score); } // Iterate over mutable values for score in scores.values_mut() { *score += 5; } println!("Updated scores: {:?}", scores); // Modify while iterating for (name, score) in &mut scores { if *score > 90 { println!("{} has an A", name); } } // Drain the map for (name, score) in scores.drain() { println!("Drained: {} -> {}", name, score); } println!("Empty map: {:?}", scores); } Working with Strings
fn main() { let text = "Hello, World!"; // Iterate over characters for c in text.chars() { print!("{} ", c); } println!(); // Iterate over words for word in text.split_whitespace() { println!("Word: {}", word); } // Iterate over lines let multiline = "Line 1\nLine 2\nLine 3"; for line in multiline.lines() { println!("Line: {}", line); } // Iterate over bytes for byte in text.bytes() { print!("{} ", byte); } println!(); // Iterate over graphemes (requires unicode-segmentation crate) // for grapheme in text.graphemes(true) { // println!("Grapheme: {}", grapheme); // } // Build string with loop let mut result = String::new(); for i in 0..5 { result.push_str(&format!("Line {}\n", i)); } println!("Result:\n{}", result); } 7. Nested For Loops
Basic Nested Loops
fn main() { // Multiplication table for i in 1..=5 { for j in 1..=5 { print!("{:4}", i * j); } println!(); } // 2D array iteration let matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]; for row in &matrix { for val in row { print!("{} ", val); } println!(); } // Triangle pattern for i in 1..=5 { for _ in 0..i { print!("* "); } println!(); } // Nested loops with enumerate for (i, row) in matrix.iter().enumerate() { for (j, val) in row.iter().enumerate() { println!("matrix[{}][{}] = {}", i, j, val); } } } Cartesian Product and Combinations
fn main() { // Cartesian product let colors = ["red", "green", "blue"]; let sizes = ["S", "M", "L"]; for &color in &colors { for &size in &sizes { println!("{} {}", color, size); } } // All pairs (combinations with repetition) let numbers = [1, 2, 3]; for &a in &numbers { for &b in &numbers { println!("({}, {})", a, b); } } // Unique pairs (combinations without repetition) for i in 0..numbers.len() { for j in i + 1..numbers.len() { println!("({}, {})", numbers[i], numbers[j]); } } // Nested loops with conditions for i in 1..=3 { for j in 1..=3 { if i + j == 4 { println!("{}+{} = 4", i, j); } } } } 8. For Loop with Ranges and Steps
Advanced Range Usage
fn main() { // Different range types let exclusive = 0..5; // 0,1,2,3,4 let inclusive = 0..=5; // 0,1,2,3,4,5 let from = 3..; // 3,4,5,... (infinite) let to = ..5; // 0,1,2,3,4 (requires start) // Using step_by for i in (0..=20).step_by(3) { println!("Step 3: {}", i); } // Reverse ranges for i in (0..=10).rev() { println!("Reverse: {}", i); } // Combining step and reverse for i in (0..=20).rev().step_by(3) { println!("Reverse step 3: {}", i); } // Custom step function fn step_range(start: i32, end: i32, step: i32) -> impl Iterator<Item = i32> { (start..=end).filter(move |&x| (x - start) % step == 0) } for i in step_range(0, 20, 4) { println!("Custom step: {}", i); } // Chaining ranges let combined = (1..=3).chain(10..=12); for i in combined { println!("Chained: {}", i); } } Infinite Ranges and Take
fn main() { // Infinite range with take for i in (0..).take(5) { println!("First 5: {}", i); } // Infinite step range for i in (0..).step_by(3).take(5) { println!("Multiples of 3: {}", i); } // Cycle through iterator let colors = ["red", "green", "blue"]; for color in colors.iter().cycle().take(10) { println!("Color: {}", color); } // Fibonacci with infinite iterator let fib = std::iter::successors(Some((0, 1)), |&(a, b)| Some((b, a + b))) .map(|(a, _)| a); for num in fib.take(10) { println!("Fib: {}", num); } // Generate with iterate let powers = std::iter::successors(Some(1), |&n| Some(n * 2)); for num in powers.take(10) { println!("Power of 2: {}", num); } } 9. For Loop with Functional Programming
Using Iterator Adaptors
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Map let doubled: Vec<_> = numbers.iter().map(|x| x * 2).collect(); println!("Doubled: {:?}", doubled); // Filter let evens: Vec<_> = numbers.iter().filter(|&&x| x % 2 == 0).collect(); println!("Evens: {:?}", evens); // Filter and map let doubled_evens: Vec<_> = numbers.iter() .filter(|&&x| x % 2 == 0) .map(|&x| x * 2) .collect(); println!("Doubled evens: {:?}", doubled_evens); // Fold (reduce) let sum = numbers.iter().fold(0, |acc, &x| acc + x); println!("Sum: {}", sum); // Any and all let has_even = numbers.iter().any(|&x| x % 2 == 0); let all_positive = numbers.iter().all(|&x| x > 0); println!("Has even: {}, All positive: {}", has_even, all_positive); // Find if let Some(first_even) = numbers.iter().find(|&&x| x % 2 == 0) { println!("First even: {}", first_even); } // Position if let Some(pos) = numbers.iter().position(|&x| x == 5) { println!("5 found at position: {}", pos); } // Min and max println!("Min: {:?}", numbers.iter().min()); println!("Max: {:?}", numbers.iter().max()); } Chaining Operations
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Complex pipeline let result: i32 = numbers.iter() .filter(|&&x| x % 2 == 0) // Keep evens .map(|&x| x * x) // Square them .filter(|&x| x > 20) // Keep >20 .take(2) // Take first 2 .sum(); // Sum them println!("Pipeline result: {}", result); // Working with Option let optional_numbers = vec![Some(1), None, Some(2), Some(3), None, Some(4)]; let sum: i32 = optional_numbers.iter() .filter_map(|&x| x) // Keep only Some values .map(|x| x * 2) // Double them .sum(); // Sum println!("Filter map sum: {}", sum); // Flat map let words = vec!["hello", "world"]; let chars: Vec<char> = words.iter() .flat_map(|&s| s.chars()) // Flatten chars .collect(); println!("Chars: {:?}", chars); // Zip let names = vec!["Alice", "Bob", "Charlie"]; let ages = vec![30, 25, 35]; let people: Vec<_> = names.iter() .zip(ages.iter()) .collect(); println!("People: {:?}", people); } 10. Performance Considerations
Loop Optimization
fn main() { // Pre-allocate capacity for better performance let mut vec = Vec::with_capacity(1000); for i in 0..1000 { vec.push(i); } // Cache length for repeated bounds checks let arr = [1, 2, 3, 4, 5]; let len = arr.len(); // Cache the length for i in 0..len { println!("arr[{}] = {}", i, arr[i]); } // Using iterators is usually as fast as manual loops let numbers: Vec<i32> = (0..1000).collect(); // Manual loop let start = std::time::Instant::now(); let mut sum1 = 0; for i in 0..numbers.len() { sum1 += numbers[i]; } println!("Manual loop: {:?}", start.elapsed()); // Iterator let start = std::time::Instant::now(); let sum2: i32 = numbers.iter().sum(); println!("Iterator: {:?}", start.elapsed()); // For large collections, consider parallel iterators with rayon // use rayon::prelude::*; // let sum3: i32 = numbers.par_iter().sum(); } Memory Access Patterns
fn main() { // Row-major vs column-major access let size = 1000; let matrix = vec![vec![0; size]; size]; // Good: Row-major (cache-friendly) let start = std::time::Instant::now(); let mut sum1 = 0; for i in 0..size { for j in 0..size { sum1 += matrix[i][j]; } } println!("Row-major: {:?}", start.elapsed()); // Bad: Column-major (cache- unfriendly) let start = std::time::Instant::now(); let mut sum2 = 0; for j in 0..size { for i in 0..size { sum2 += matrix[i][j]; } } println!("Column-major: {:?}", start.elapsed()); // Using iterators is often optimized let start = std::time::Instant::now(); let sum3: i32 = matrix.iter() .flat_map(|row| row.iter()) .sum(); println!("Iterator: {:?}", start.elapsed()); } 11. For Loop with Concurrency
Rayon Parallel Iterators
// Add rayon = "1.7" to Cargo.toml use rayon::prelude::*; fn main() { let numbers: Vec<i32> = (0..1_000_000).collect(); // Sequential sum let start = std::time::Instant::now(); let sum_seq: i32 = numbers.iter().sum(); println!("Sequential sum: {:?}", start.elapsed()); // Parallel sum let start = std::time::Instant::now(); let sum_par: i32 = numbers.par_iter().sum(); println!("Parallel sum: {:?}", start.elapsed()); // Parallel map and filter let results: Vec<_> = numbers.par_iter() .filter(|&&x| x % 2 == 0) .map(|&x| x * x) .collect(); println!("Parallel results length: {}", results.len()); // Parallel fold let sum = numbers.par_iter() .fold(|| 0, |acc, &x| acc + x) .sum::<i32>(); println!("Parallel fold sum: {}", sum); } Channels and For Loops
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); // Spawn producer thread let producer = thread::spawn(move || { for i in 0..10 { tx.send(i).unwrap(); thread::sleep(std::time::Duration::from_millis(100)); } }); // Receive in for loop for received in rx { println!("Got: {}", received); } producer.join().unwrap(); // Multiple producers let (tx, rx) = mpsc::channel(); let mut handles = vec![]; for id in 0..3 { let tx_clone = tx.clone(); handles.push(thread::spawn(move || { for i in 0..5 { tx_clone.send(format!("Thread {}: {}", id, i)).unwrap(); thread::sleep(std::time::Duration::from_millis(50)); } })); } // Drop original sender drop(tx); // Receive from all threads for message in rx { println!("{}", message); } for handle in handles { handle.join().unwrap(); } } 12. For Loop with Error Handling
Handling Results in Loops
use std::fs::File; use std::io::{self, BufRead}; fn main() -> io::Result<()> { // Reading lines from a file let file = File::open("example.txt")?; let reader = io::BufReader::new(file); for (index, line) in reader.lines().enumerate() { match line { Ok(content) => println!("Line {}: {}", index + 1, content), Err(e) => eprintln!("Error reading line {}: {}", index + 1, e), } } // Collecting Results into a Result<Vec> let numbers = vec!["42", "24", "abc", "12"]; let parsed: Result<Vec<i32>, _> = numbers.iter() .map(|s| s.parse::<i32>()) .collect(); match parsed { Ok(nums) => println!("Parsed numbers: {:?}", nums), Err(e) => println!("Parse error: {}", e), } // Filtering out errors let successful: Vec<i32> = numbers.iter() .filter_map(|s| s.parse::<i32>().ok()) .collect(); println!("Successful: {:?}", successful); // Short-circuit on first error let result: Result<Vec<i32>, _> = numbers.iter() .map(|s| s.parse::<i32>()) .collect::<Result<Vec<_>, _>>(); match result { Ok(nums) => println!("All valid: {:?}", nums), Err(e) => println!("First error: {}", e), } Ok(()) } Fallible Operations in Loops
use std::fs; use std::io; fn process_files() -> io::Result<()> { let filenames = vec!["file1.txt", "file2.txt", "file3.txt"]; let mut errors = Vec::new(); for filename in &filenames { match fs::read_to_string(filename) { Ok(content) => println!("{}: {} bytes", filename, content.len()), Err(e) => errors.push((filename, e)), } } if !errors.is_empty() { for (filename, error) in errors { eprintln!("Error reading {}: {}", filename, error); } } Ok(()) } // Using try_blocks for early return on error #![feature(try_blocks)] fn process_all_or_none() -> Result<Vec<String>, io::Error> { let filenames = vec!["file1.txt", "file2.txt", "file3.txt"]; let result: Result<Vec<_>, _> = try { let mut contents = Vec::new(); for filename in filenames { contents.push(fs::read_to_string(filename)?); } contents }; result } fn main() { match process_files() { Ok(()) => println!("Processing complete"), Err(e) => println!("IO error: {}", e), } // This will return at first error match process_all_or_none() { Ok(contents) => println!("All files read: {} files", contents.len()), Err(e) => println!("Failed: {}", e), } } 13. Advanced For Loop Patterns
Custom Loop Transformations
fn main() { // Pairs (window of size 2) let numbers = vec![1, 2, 3, 4, 5]; for window in numbers.windows(2) { println!("Window: {:?}", window); println!("Sum: {}", window[0] + window[1]); } // Chunks let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8]; for chunk in numbers.chunks(3) { println!("Chunk: {:?}", chunk); } // Chunks with mutable access let mut numbers = vec![1, 2, 3, 4, 5, 6]; for chunk in numbers.chunks_mut(2) { for num in chunk.iter_mut() { *num *= 2; } } println!("After chunks_mut: {:?}", numbers); // Group by (requires itertools) // use itertools::Itertools; // for (key, group) in &numbers.iter().group_by(|&&x| x % 2 == 0) { // println!("Group {}: {:?}", key, group.collect::<Vec<_>>()); // } } Stateful Iteration
struct StateMachine { state: i32, } impl StateMachine { fn new() -> Self { StateMachine { state: 0 } } } impl Iterator for StateMachine { type Item = i32; fn next(&mut self) -> Option<Self::Item> { self.state += 1; if self.state <= 5 { Some(self.state) } else { None } } } fn main() { // Scan (stateful map) let numbers = vec![1, 2, 3, 4, 5]; let running_sum: Vec<_> = numbers.iter() .scan(0, |acc, &x| { *acc += x; Some(*acc) }) .collect(); println!("Running sum: {:?}", running_sum); // Custom stateful iterator for state in StateMachine::new() { println!("State: {}", state); } // Fold with state let sum = numbers.iter() .fold(0, |acc, &x| acc + x); println!("Fold sum: {}", sum); // Try_fold (short-circuiting fold) let result = numbers.iter() .try_fold(0, |acc, &x| { if x == 3 { None } else { Some(acc + x) } }); match result { Some(sum) => println!("Sum until error: {}", sum), None => println!("Stopped at 3"), } } Conclusion
The for loop in Rust is a versatile and powerful construct that goes far beyond simple iteration:
Key Takeaways
- Iterator-based: Works seamlessly with any iterable type
- Ownership-aware: Respects borrowing rules with
.iter(),.iter_mut(),.into_iter() - Range syntax:
0..5(exclusive) and0..=5(inclusive) ranges - Pattern matching: Destructure tuples, structs, and enums directly
- Control flow:
break,continue, and loop labels - Functional composition: Chain iterator adaptors for complex operations
- Zero-cost: Compiles to efficient code comparable to manual loops
Common Patterns
- Range iteration:
for i in 0..n - Collection iteration:
for item in &collection - Indexed iteration:
for (i, item) in collection.iter().enumerate() - Nested loops: With labels for breaking outer loops
- Parallel iteration: Using rayon for concurrent processing
- Error handling: Collecting Results or short-circuiting
Best Practices
- Prefer iterators over manual indexing when possible
- Use
enumerate()when you need indices - Choose the right ownership: Use
&collection,&mut collection, orcollectionappropriately - Chain adaptors for complex transformations
- Cache collection lengths when using manual indexing
- Use
breakwith labels for nested loops - Consider parallel iterators for CPU-bound operations on large datasets
The for loop in Rust embodies the language's philosophy of combining safety, expressiveness, and performance, making it an essential tool in every Rust programmer's toolkit.