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.