Complete Guide to Rust For Loops

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

  1. Iterator-based: Works seamlessly with any iterable type
  2. Ownership-aware: Respects borrowing rules with .iter(), .iter_mut(), .into_iter()
  3. Range syntax: 0..5 (exclusive) and 0..=5 (inclusive) ranges
  4. Pattern matching: Destructure tuples, structs, and enums directly
  5. Control flow: break, continue, and loop labels
  6. Functional composition: Chain iterator adaptors for complex operations
  7. 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

  1. Prefer iterators over manual indexing when possible
  2. Use enumerate() when you need indices
  3. Choose the right ownership: Use &collection, &mut collection, or collection appropriately
  4. Chain adaptors for complex transformations
  5. Cache collection lengths when using manual indexing
  6. Use break with labels for nested loops
  7. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper