Introduction to Loops in Rust
Loops are fundamental control flow structures that allow code execution to repeat multiple times. Rust provides several powerful looping constructs, each designed for different scenarios. Understanding loops is essential for writing efficient, readable, and idiomatic Rust code.
Types of Loops in Rust
loop: Infinite loops that can break with a valuewhile: Condition-based loopsfor: Collection iteration loops (most common)while let: Pattern-matching loops
Key Concepts
- Iteration: Repeating code execution
- Control flow:
break,continue, and loop labels - Performance: Zero-cost abstractions
- Safety: Bounds checking and iterator guarantees
- Expressiveness: Different loops for different purposes
1. The loop Keyword
Basic Infinite Loops
fn main() {
// Basic infinite loop
let mut counter = 0;
loop {
println!("Iteration {}", counter);
counter += 1;
if counter >= 5 {
break; // Exit the loop
}
}
// Loop with condition inside
let mut x = 10;
loop {
x -= 1;
if x == 0 {
break;
}
println!("x = {}", x);
}
}
Returning Values from Loops
fn main() {
// Loop can return a value with break
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // Returns 20
}
};
println!("Result from loop: {}", result); // 20
// Practical example: finding a value
let numbers = [1, 3, 5, 7, 9, 11, 13];
let mut index = 0;
let found = loop {
if index >= numbers.len() {
break None;
}
if numbers[index] == 7 {
break Some(index);
}
index += 1;
};
match found {
Some(i) => println!("Found 7 at index {}", i),
None => println!("7 not found"),
}
}
Nested Loops and Labels
fn main() {
// Nested loops with labels
let mut count = 0;
'outer: loop {
println!("Outer loop: count = {}", count);
let mut remaining = 3;
'inner: loop {
println!(" Inner loop: remaining = {}", remaining);
remaining -= 1;
if remaining == 0 {
break 'inner; // Break inner loop
}
if count == 2 {
println!(" Breaking outer loop");
break 'outer; // Break outer loop
}
}
count += 1;
}
// Another example with nested loops
let mut matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let target = 5;
let mut found_at = None;
'search: for (i, row) in matrix.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
if value == target {
found_at = Some((i, j));
break 'search;
}
}
}
println!("Found at: {:?}", found_at);
}
2. The while Loop
Basic While Loops
fn main() {
// Simple while loop
let mut count = 0;
while count < 5 {
println!("count = {}", count);
count += 1;
}
// While with complex condition
let mut x = 10;
let mut y = 0;
while x > 0 && y < 5 {
println!("x: {}, y: {}", x, y);
x -= 2;
y += 1;
}
// While with break
let mut numbers = vec![1, 2, 3, 4, 5];
let mut sum = 0;
while let Some(num) = numbers.pop() {
sum += num;
if sum > 10 {
println!("Sum exceeded 10: {}", sum);
break;
}
}
}
While vs Loop
fn main() {
// When to use loop vs while
// Use loop for infinite or conditional break inside
let mut count = 0;
loop {
count += 1;
if count >= 5 {
break;
}
}
// Use while for condition-based looping
let mut count = 0;
while count < 5 {
count += 1;
}
// Both compile to similar code, but while is more idiomatic
// for simple condition checks
// Performance comparison (usually identical)
let mut i = 0;
while i < 1_000_000 {
i += 1;
}
let mut i = 0;
loop {
if i >= 1_000_000 {
break;
}
i += 1;
}
}
3. The for Loop
Basic For Loops
fn main() {
// For loop over range
for i in 0..5 {
println!("i = {}", i);
}
// Inclusive range
for i in 1..=5 {
println!("i (inclusive) = {}", i);
}
// For loop with step (using step_by)
for i in (0..10).step_by(2) {
println!("Even: {}", i);
}
// Reverse range
for i in (0..5).rev() {
println!("Reverse: {}", i);
}
// For loop with enumerate
let items = vec!["a", "b", "c", "d"];
for (index, value) in items.iter().enumerate() {
println!("items[{}] = {}", index, value);
}
// For loop over array
let array = [10, 20, 30, 40, 50];
for element in array.iter() {
println!("Element: {}", element);
}
// Mutable iteration
let mut numbers = vec![1, 2, 3, 4, 5];
for num in numbers.iter_mut() {
*num *= 2;
}
println!("Doubled: {:?}", numbers);
}
For Loops with Collections
fn main() {
// Vector iteration
let vec = vec!["apple", "banana", "cherry"];
// By reference (most common)
for fruit in &vec {
println!("Fruit: {}", fruit);
}
// By value (consumes the vector)
for fruit in vec {
println!("Fruit (consumed): {}", fruit);
}
// println!("{:?}", vec); // Error: vec moved
// HashMap iteration
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("Alice", 25);
map.insert("Bob", 30);
map.insert("Charlie", 35);
for (name, age) in &map {
println!("{} is {} years old", name, age);
}
// String iteration
let text = "Hello, World!";
for c in text.chars() {
print!("{} ", c);
}
println!();
for word in text.split_whitespace() {
println!("Word: {}", word);
}
// Custom collection iteration
#[derive(Debug)]
struct Range {
start: i32,
end: i32,
}
impl Range {
fn iter(&self) -> RangeIter {
RangeIter {
current: self.start,
end: self.end,
}
}
}
struct RangeIter {
current: i32,
end: i32,
}
impl Iterator for RangeIter {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.end {
let value = self.current;
self.current += 1;
Some(value)
} else {
None
}
}
}
let range = Range { start: 5, end: 10 };
for i in range.iter() {
println!("Custom range: {}", i);
}
}
4. The while let Loop
Pattern Matching Loops
fn main() {
// while let with Option
let mut numbers = vec![1, 2, 3, 4, 5];
while let Some(num) = numbers.pop() {
println!("Popped: {}", num);
}
// while let with Result
let mut results = vec![Ok(1), Ok(2), Err("error"), Ok(3)];
while let Ok(num) = results.pop() {
println!("Success: {}", num);
}
// while let with custom enum
enum State {
Processing(i32),
Done,
}
let mut state = State::Processing(0);
while let State::Processing(count) = state {
println!("Processing: {}", count);
if count >= 5 {
state = State::Done;
} else {
state = State::Processing(count + 1);
}
}
// while let with multiple patterns
let mut pairs = vec![(1, 2), (3, 4), (5, 6)];
while let Some((x, y)) = pairs.pop() {
println!("Pair: ({}, {})", x, y);
}
// while let with references
let mut data = vec![Some(1), Some(2), None, Some(3)];
let mut iter = data.iter_mut();
while let Some(Some(value)) = iter.next() {
*value *= 10;
}
println!("Modified: {:?}", data);
}
Advanced while let Patterns
fn main() {
// while let with destructuring
#[derive(Debug)]
struct Container {
items: Vec<Option<i32>>,
}
let mut containers = vec![
Container { items: vec![Some(1), None, Some(2)] },
Container { items: vec![Some(3), Some(4)] },
];
while let Some(container) = containers.pop() {
println!("Processing container: {:?}", container);
let mut items = container.items;
while let Some(Some(val)) = items.pop() {
println!(" Value: {}", val);
}
}
// while let with guards (using match inside)
let mut numbers = vec![1, 2, 3, 4, 5, 6];
while let Some(num) = numbers.pop() {
match num {
n if n % 2 == 0 => println!("Even: {}", n),
_ => continue,
}
}
// Nested while let
let mut nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
while let Some(mut inner) = nested.pop() {
println!("Outer item");
while let Some(val) = inner.pop() {
println!(" Inner: {}", val);
}
}
}
5. Loop Control: break and continue
Break Statement
fn main() {
// Basic break
let mut count = 0;
loop {
count += 1;
if count == 5 {
break;
}
println!("Count: {}", count);
}
// Break with value
let mut count = 0;
let result = loop {
count += 1;
if count == 5 {
break count * 2; // Returns 10
}
};
println!("Result: {}", result);
// Break in nested loops
'outer: for i in 0..5 {
for j in 0..5 {
if i == 2 && j == 3 {
println!("Breaking at ({}, {})", i, j);
break 'outer;
}
println!("i: {}, j: {}", i, j);
}
}
// Break in while loop
let mut numbers = vec![1, 2, 3, 4, 5];
let target = 3;
while let Some(num) = numbers.pop() {
if num == target {
println!("Found target: {}", target);
break;
}
println!("Popped: {}", num);
}
}
Continue Statement
fn main() {
// Basic continue
for i in 0..10 {
if i % 2 == 0 {
continue; // Skip even numbers
}
println!("Odd: {}", i);
}
// Continue in nested loops
'outer: for i in 0..3 {
for j in 0..3 {
if i == 1 && j == 1 {
println!("Skipping i={}, j={}", i, j);
continue 'outer; // Continue outer loop
}
println!("i: {}, j: {}", i, j);
}
}
// Continue with while
let mut data = vec![1, 2, 3, 4, 5];
let mut sum = 0;
while let Some(num) = data.pop() {
if num == 3 {
println!("Skipping 3");
continue;
}
sum += num;
println!("Added {}, sum = {}", num, sum);
}
// Continue in iterator chain
let numbers = vec![1, 2, 3, 4, 5, 6];
let mut even_squares = Vec::new();
for &num in &numbers {
if num % 2 != 0 {
continue;
}
even_squares.push(num * num);
}
println!("Even squares: {:?}", even_squares);
}
6. Iterators and For Loops
Iterator Adaptors
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map - transform each element
let squares: Vec<_> = numbers.iter()
.map(|&x| x * x)
.collect();
println!("Squares: {:?}", squares);
// filter - keep elements matching condition
let evens: Vec<_> = numbers.iter()
.filter(|&&x| x % 2 == 0)
.collect();
println!("Evens: {:?}", evens);
// filter_map - combine filter and map
let strings = vec!["1", "2", "three", "4", "five"];
let parsed: Vec<i32> = strings.iter()
.filter_map(|&s| s.parse().ok())
.collect();
println!("Parsed numbers: {:?}", parsed);
// take - limit number of elements
let first_three: Vec<_> = numbers.iter()
.take(3)
.collect();
println!("First three: {:?}", first_three);
// skip - skip elements
let after_five: Vec<_> = numbers.iter()
.skip(5)
.collect();
println!("After five: {:?}", after_five);
// zip - combine two iterators
let names = vec!["Alice", "Bob", "Charlie"];
let ages = vec![25, 30, 35];
let people: Vec<_> = names.iter()
.zip(ages.iter())
.collect();
println!("People: {:?}", people);
// chain - concatenate iterators
let first = vec![1, 2, 3];
let second = vec![4, 5, 6];
let combined: Vec<_> = first.iter()
.chain(second.iter())
.collect();
println!("Combined: {:?}", combined);
}
Consuming Iterators
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// count - number of elements
let count = numbers.iter().count();
println!("Count: {}", count);
// sum - sum of elements
let sum: i32 = numbers.iter().sum();
println!("Sum: {}", sum);
// product - product of elements
let product: i32 = numbers.iter().product();
println!("Product: {}", product);
// max - maximum element
let max = numbers.iter().max();
println!("Max: {:?}", max);
// min - minimum element
let min = numbers.iter().min();
println!("Min: {:?}", min);
// fold - accumulate with initial value
let sum = numbers.iter().fold(0, |acc, &x| acc + x);
println!("Fold sum: {}", sum);
let factorial = numbers.iter().fold(1, |acc, &x| acc * x);
println!("Fold product: {}", factorial);
// any - check if any element matches
let has_even = numbers.iter().any(|&x| x % 2 == 0);
println!("Has even: {}", has_even);
// all - check if all elements match
let all_positive = numbers.iter().all(|&x| x > 0);
println!("All positive: {}", all_positive);
// find - find first matching element
let first_even = numbers.iter().find(|&&x| x % 2 == 0);
println!("First even: {:?}", first_even);
// position - find index of first match
let pos = numbers.iter().position(|&x| x > 3);
println!("Position of first >3: {:?}", pos);
}
7. Custom Iterators
Implementing Iterator
struct Fibonacci {
current: u64,
next: u64,
}
impl Fibonacci {
fn new() -> Self {
Fibonacci {
current: 0,
next: 1,
}
}
}
impl Iterator for Fibonacci {
type Item = u64;
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)
}
}
struct PrimeIterator {
current: u64,
}
impl PrimeIterator {
fn new() -> Self {
PrimeIterator { current: 2 }
}
fn is_prime(n: u64) -> bool {
if n <= 1 {
return false;
}
if n == 2 {
return true;
}
if n % 2 == 0 {
return false;
}
let mut i = 3;
while i * i <= n {
if n % i == 0 {
return false;
}
i += 2;
}
true
}
}
impl Iterator for PrimeIterator {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
while !PrimeIterator::is_prime(self.current) {
self.current += 1;
}
let prime = self.current;
self.current += 1;
Some(prime)
}
}
fn main() {
// Fibonacci iterator
println!("First 10 Fibonacci numbers:");
for (i, num) in Fibonacci::new().take(10).enumerate() {
println!("fib[{}] = {}", i + 1, num);
}
// Prime iterator
println!("\nFirst 10 prime numbers:");
for (i, prime) in PrimeIterator::new().take(10).enumerate() {
println!("prime[{}] = {}", i + 1, prime);
}
// Using iterator methods
let fib_sum: u64 = Fibonacci::new().take(10).sum();
println!("\nSum of first 10 Fibonacci numbers: {}", fib_sum);
let first_prime_above_100 = PrimeIterator::new()
.find(|&p| p > 100);
println!("First prime above 100: {:?}", first_prime_above_100);
}
Iterator Combinators
struct StepBy<I> {
iter: I,
step: usize,
first: bool,
}
impl<I: Iterator> StepBy<I> {
fn new(iter: I, step: usize) -> Self {
StepBy {
iter,
step,
first: true,
}
}
}
impl<I: Iterator> Iterator for StepBy<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
self.first = false;
self.iter.next()
} else {
self.iter.nth(self.step - 1)
}
}
}
struct Interleave<I, J> {
iter1: I,
iter2: J,
toggle: bool,
}
impl<I, J> Interleave<I, J> {
fn new(iter1: I, iter2: J) -> Self {
Interleave {
iter1,
iter2,
toggle: true,
}
}
}
impl<I, J> Iterator for Interleave<I, J>
where
I: Iterator,
J: Iterator<Item = I::Item>,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.toggle = !self.toggle;
if self.toggle {
self.iter1.next()
} else {
self.iter2.next()
}
}
}
fn main() {
let numbers = 0..10;
// Custom step_by
let step_by = StepBy::new(numbers, 3);
println!("Step by 3: {:?}", step_by.collect::<Vec<_>>());
// Interleave two iterators
let evens = (0..10).filter(|&x| x % 2 == 0);
let odds = (0..10).filter(|&x| x % 2 != 0);
let interleaved = Interleave::new(evens, odds);
println!("Interleaved evens and odds: {:?}",
interleaved.take(10).collect::<Vec<_>>());
}
8. Performance and Optimization
Loop Optimizations
fn main() {
use std::time::Instant;
let data: Vec<i32> = (0..1_000_000).collect();
// For loop with iterator (idiomatic)
let start = Instant::now();
let mut sum1 = 0;
for &x in &data {
sum1 += x;
}
let time1 = start.elapsed();
// While loop with index
let start = Instant::now();
let mut sum2 = 0;
let mut i = 0;
while i < data.len() {
sum2 += data[i];
i += 1;
}
let time2 = start.elapsed();
// Loop with iterator and sum()
let start = Instant::now();
let sum3: i32 = data.iter().sum();
let time3 = start.elapsed();
println!("For loop: {:?}, sum = {}", time1, sum1);
println!("While loop: {:?}, sum = {}", time2, sum2);
println!("Iterator sum: {:?}, sum = {}", time3, sum3);
// Cache-friendly vs cache-unfriendly
let matrix_size = 1000;
let mut matrix = vec![vec![0; matrix_size]; matrix_size];
// Row-major (cache-friendly)
let start = Instant::now();
for i in 0..matrix_size {
for j in 0..matrix_size {
matrix[i][j] += 1;
}
}
println!("Row-major: {:?}", start.elapsed());
// Column-major (cache-unfriendly)
let start = Instant::now();
for j in 0..matrix_size {
for i in 0..matrix_size {
matrix[i][j] += 1;
}
}
println!("Column-major: {:?}", start.elapsed());
}
Loop Unrolling
fn main() {
let data: Vec<i32> = (0..1000).collect();
// Standard loop
let start = std::time::Instant::now();
let mut sum1 = 0;
for &x in &data {
sum1 += x;
}
let time1 = start.elapsed();
// Manually unrolled (rarely needed, compiler does this)
let start = std::time::Instant::now();
let mut sum2 = 0;
let chunks = data.chunks(4);
for chunk in chunks {
if chunk.len() == 4 {
sum2 += chunk[0] + chunk[1] + chunk[2] + chunk[3];
} else {
for &x in chunk {
sum2 += x;
}
}
}
let time2 = start.elapsed();
println!("Standard loop: {:?}", time1);
println!("Unrolled loop: {:?}", time2);
// Using iterators with adaptors (compiler optimizes)
let start = std::time::Instant::now();
let sum3: i32 = data.par_iter().sum(); // Parallel iteration with rayon
let time3 = start.elapsed();
println!("Parallel sum: {:?}", time3);
}
9. Common Loop Patterns
Early Return Pattern
fn find_first_positive(numbers: &[i32]) -> Option<i32> {
for &num in numbers {
if num > 0 {
return Some(num);
}
}
None
}
fn all_positive(numbers: &[i32]) -> bool {
for &num in numbers {
if num <= 0 {
return false;
}
}
true
}
fn process_until_error(data: &[Result<i32, &str>]) -> Vec<i32> {
let mut results = Vec::new();
for item in data {
match item {
Ok(val) => results.push(*val),
Err(_) => break,
}
}
results
}
fn main() {
let numbers = vec![-1, -2, 3, 4, -5];
if let Some(first) = find_first_positive(&numbers) {
println!("First positive: {}", first);
}
println!("All positive: {}", all_positive(&numbers));
let data = vec![Ok(1), Ok(2), Err("error"), Ok(3)];
let processed = process_until_error(&data);
println!("Processed until error: {:?}", processed);
}
Accumulator Pattern
fn running_total(numbers: &[i32]) -> Vec<i32> {
let mut result = Vec::with_capacity(numbers.len());
let mut total = 0;
for &num in numbers {
total += num;
result.push(total);
}
result
}
fn cumulative_product(numbers: &[i32]) -> Vec<i32> {
let mut result = Vec::with_capacity(numbers.len());
let mut product = 1;
for &num in numbers {
product *= num;
result.push(product);
}
result
}
fn moving_average(numbers: &[f64], window: usize) -> Vec<f64> {
if numbers.len() < window {
return Vec::new();
}
let mut result = Vec::new();
for i in 0..=numbers.len() - window {
let window_slice = &numbers[i..i + window];
let avg = window_slice.iter().sum::<f64>() / window as f64;
result.push(avg);
}
result
}
fn main() {
let nums = vec![1, 2, 3, 4, 5];
println!("Running total: {:?}", running_total(&nums));
println!("Cumulative product: {:?}", cumulative_product(&nums));
let floats = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
println!("Moving average (window=3): {:?}", moving_average(&floats, 3));
}
Window Processing
fn sliding_window_max(numbers: &[i32], window_size: usize) -> Vec<i32> {
if numbers.len() < window_size {
return Vec::new();
}
let mut result = Vec::new();
for window in numbers.windows(window_size) {
let max = *window.iter().max().unwrap();
result.push(max);
}
result
}
fn adjacent_differences(numbers: &[i32]) -> Vec<i32> {
numbers.windows(2)
.map(|w| w[1] - w[0])
.collect()
}
fn local_maxima(numbers: &[i32]) -> Vec<(usize, i32)> {
numbers.windows(3)
.enumerate()
.filter_map(|(i, w)| {
if w[1] > w[0] && w[1] > w[2] {
Some((i + 1, w[1]))
} else {
None
}
})
.collect()
}
fn main() {
let numbers = vec![1, 3, 2, 5, 4, 7, 6, 9, 8];
println!("Window max (size 3): {:?}",
sliding_window_max(&numbers, 3));
println!("Adjacent differences: {:?}",
adjacent_differences(&numbers));
println!("Local maxima: {:?}",
local_maxima(&numbers));
}
10. Loops with Concurrency
Parallel Processing
use std::thread;
use std::sync::{Arc, Mutex, mpsc};
fn parallel_map<T, F>(data: Vec<T>, f: F) -> Vec<T::Item>
where
T: Send + 'static,
T::Item: Send + 'static,
F: Fn(T) -> T::Item + Send + Sync + Clone + 'static,
{
let (tx, rx) = mpsc::channel();
let data = Arc::new(Mutex::new(data));
let num_threads = 4;
let mut handles = vec![];
for _ in 0..num_threads {
let data = data.clone();
let tx = tx.clone();
let f = f.clone();
handles.push(thread::spawn(move || {
loop {
let item = {
let mut data = data.lock().unwrap();
if data.is_empty() {
break;
}
data.pop()
};
if let Some(item) = item {
let result = f(item);
tx.send(result).unwrap();
}
}
}));
}
drop(tx); // Close channel
let mut results = vec![];
for received in rx {
results.push(received);
}
for handle in handles {
handle.join().unwrap();
}
results
}
fn main() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Parallel map
let results = parallel_map(data, |x| {
thread::sleep(std::time::Duration::from_millis(10));
x * x
});
println!("Parallel map results: {:?}", results);
// Channel-based work distribution
let (tx, rx) = mpsc::channel();
// Producer
let producer = thread::spawn(move || {
for i in 0..10 {
tx.send(i).unwrap();
thread::sleep(std::time::Duration::from_millis(50));
}
});
// Consumer with loop
let consumer = thread::spawn(move || {
let mut sum = 0;
for received in rx {
println!("Received: {}", received);
sum += received;
}
sum
});
producer.join().unwrap();
let result = consumer.join().unwrap();
println!("Sum of all received: {}", result);
}
11. Error Handling in Loops
Result Handling
use std::num::ParseIntError;
fn parse_numbers(strings: &[&str]) -> Result<Vec<i32>, ParseIntError> {
let mut numbers = Vec::new();
for &s in strings {
let num = s.parse::<i32>()?;
numbers.push(num);
}
Ok(numbers)
}
fn process_with_retry<F, T>(mut f: F, max_retries: usize) -> Result<T, String>
where
F: FnMut() -> Result<T, String>,
{
let mut last_error = String::new();
for attempt in 1..=max_retries {
println!("Attempt {} of {}", attempt, max_retries);
match f() {
Ok(result) => return Ok(result),
Err(e) => {
last_error = e;
if attempt < max_retries {
println!("Failed: {}. Retrying...", last_error);
}
}
}
}
Err(format!("All {} attempts failed: {}", max_retries, last_error))
}
fn collect_valid_numbers(input: &[&str]) -> Vec<i32> {
let mut valid = Vec::new();
for &s in input {
if let Ok(num) = s.parse::<i32>() {
valid.push(num);
}
}
valid
}
fn main() {
let strings = vec!["1", "2", "three", "4", "five"];
match parse_numbers(&strings) {
Ok(nums) => println!("Parsed: {:?}", nums),
Err(e) => println!("Parse error: {}", e),
}
let valid = collect_valid_numbers(&strings);
println!("Valid numbers: {:?}", valid);
// Retry example
let mut attempts = 0;
let result = process_with_retry(
|| {
attempts += 1;
if attempts < 3 {
Err(format!("Failed on attempt {}", attempts))
} else {
Ok(42)
}
},
5,
);
match result {
Ok(val) => println!("Success: {}", val),
Err(e) => println!("Failure: {}", e),
}
}
12. Loops in Functional Programming
Functional Alternatives to Loops
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Imperative approach
let mut squares_imperative = Vec::new();
for &x in &numbers {
squares_imperative.push(x * x);
}
// Functional approach
let squares_functional: Vec<i32> = numbers.iter()
.map(|&x| x * x)
.collect();
assert_eq!(squares_imperative, squares_functional);
// Filtering
let evens_imperative = Vec::new();
let mut evens = Vec::new();
for &x in &numbers {
if x % 2 == 0 {
evens.push(x);
}
}
let evens_functional: Vec<i32> = numbers.iter()
.filter(|&&x| x % 2 == 0)
.copied()
.collect();
// Reducing
let sum_imperative = {
let mut sum = 0;
for &x in &numbers {
sum += x;
}
sum
};
let sum_functional: i32 = numbers.iter().sum();
let sum_fold = numbers.iter().fold(0, |acc, &x| acc + x);
// Complex transformations
let data = vec![
"1", "2", "three", "4", "five", "6"
];
// Imperative
let mut result_imperative = Vec::new();
for s in data {
if let Ok(num) = s.parse::<i32>() {
if num % 2 == 0 {
result_imperative.push(num * num);
}
}
}
// Functional
let result_functional: Vec<i32> = data.iter()
.filter_map(|&s| s.parse::<i32>().ok())
.filter(|&num| num % 2 == 0)
.map(|num| num * num)
.collect();
println!("Imperative: {:?}", result_imperative);
println!("Functional: {:?}", result_functional);
}
Conclusion
Rust's loop constructs provide powerful and safe ways to repeat code execution:
Key Takeaways
loop: For infinite loops that need to break with valueswhile: For condition-based repetitionfor: For iterating over collections (most common)while let: For pattern-matching loops- Break/Continue: With labels for nested control flow
- Iterators: Functional approach to looping
- Performance: Zero-cost abstractions with compiler optimizations
Best Practices
- Use
forloops when iterating over collections - Use
whileloops for condition-based logic - Use
loopfor infinite loops or when returning values - Use iterators and adaptors for complex transformations
- Prefer
breakwith values over mutable state - Use loop labels for clarity in nested loops
- Consider
while letfor Option/Result iteration
Performance Tips
- Prefer iterators over manual indexing
- Use
forloops - they're idiomatic and optimized - Let the compiler unroll loops automatically
- Use
chunksorwindowsfor batch processing - Consider parallel iterators for large datasets
- Profile before optimizing - compilers are smart
Rust's loop constructs, combined with its powerful iterator system, provide everything needed to write efficient, expressive, and safe iterative code.