Introduction to Loops in Rust
Loops are fundamental control flow structures that allow you to execute a block of code multiple times. Rust provides several types of loops, each designed for different scenarios, with a strong emphasis on safety and expressiveness. Rust's loop system includes powerful features like loop labels, break with values, and integration with iterators.
Key Concepts
- Loop Types:
loop,while,forfor different use cases - Control Flow:
break,continuefor loop manipulation - Loop Labels: Named loops for nested control
- Pattern Matching: Integration with
while letand pattern matching - Iterator Integration:
forloops work with any iterator
1. The loop Keyword
Infinite Loops
fn main() {
// Basic infinite loop
let mut counter = 0;
loop {
counter += 1;
println!("Iteration: {}", counter);
if counter >= 5 {
break; // Exit the loop
}
}
// Loop with condition inside
let mut attempts = 0;
loop {
attempts += 1;
println!("Attempt {}", attempts);
// Simulate some operation
let success = attempts == 3;
if success {
println!("Success on attempt {}!", attempts);
break;
}
if attempts >= 5 {
println!("Failed after {} attempts", attempts);
break;
}
}
}
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; // Return 20 from the loop
}
};
println!("Result from loop: {}", result); // 20
// More complex example
let mut guess = 0;
let secret_number = 42;
let message = loop {
guess += 1;
if guess == secret_number {
break format!("Found it in {} guesses!", guess);
}
if guess > 100 {
break "Couldn't find it...".to_string();
}
};
println!("{}", message);
}
Nested Loops
fn main() {
// Simple nested loops
for i in 1..=3 {
for j in 1..=3 {
println!("i = {}, j = {}", i, j);
}
}
// Nested with conditions
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
'outer: for i in 0..3 {
for j in 0..3 {
if matrix[i][j] == 5 {
println!("Found 5 at position ({}, {})", i, j);
break 'outer; // Break outer loop
}
println!("Checking ({}, {}) = {}", i, j, matrix[i][j]);
}
}
}
2. Loop Labels
Labeled Loops for Precise Control
fn main() {
// Basic loop labels
'outer: loop {
println!("Entered outer loop");
'inner: loop {
println!("Entered inner loop");
// break 'outer; // This would exit both loops
break 'inner; // This only exits inner loop
}
println!("Still in outer loop");
break 'outer;
}
// Nested loops with labels and conditions
let mut count = 0;
'counting_up: loop {
println!("Count = {}", count);
let mut remaining = 10;
'inner: loop {
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {}", count);
}
Labeled Break and Continue
fn main() {
// Using labels with break and continue
'outer: for i in 1..=5 {
println!("Outer iteration: {}", i);
'inner: for j in 1..=5 {
if i == 3 && j == 3 {
println!("Breaking outer loop at i={}, j={}", i, j);
break 'outer;
}
if j == 2 {
println!(" Continuing inner loop at j={}", j);
continue 'inner;
}
println!(" Inner iteration: {}", j);
}
}
// More complex example
let data = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
'search: for (i, row) in data.iter().enumerate() {
for (j, &value) in row.iter().enumerate() {
if value == 5 {
println!("Found 5 at [{}, {}]", i, j);
break 'search;
}
if value % 2 == 0 {
println!("Even number {} at [{}, {}], skipping row", value, i, j);
continue 'search; // Continue to next row
}
println!("Checking [{}, {}] = {}", i, j, value);
}
}
}
3. The while Loop
Basic While Loops
fn main() {
// Simple while loop
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
// While with complex condition
let mut x = 0;
let mut y = 10;
while x < y {
println!("x = {}, y = {}", x, y);
x += 1;
y -= 1;
}
// While with boolean flag
let mut running = true;
let mut counter = 0;
while running {
counter += 1;
println!("Running... ({})", counter);
if counter >= 5 {
running = false;
}
}
}
While with Pattern Matching
fn main() {
// while let for pattern matching
let mut optional = Some(0);
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quitting!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
}
// More practical example
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
// Working with iterators
let mut iter = 1..=5;
while let Some(value) = iter.next() {
println!("Next value: {}", value);
}
}
4. The for Loop
Basic For Loops with Ranges
fn main() {
// For loop with range (exclusive)
for i in 0..5 {
println!("i = {}", i); // 0, 1, 2, 3, 4
}
// For loop with inclusive range
for i in 1..=5 {
println!("i = {}", i); // 1, 2, 3, 4, 5
}
// Reverse range
for i in (1..=5).rev() {
println!("Reverse: {}", i); // 5, 4, 3, 2, 1
}
// Step by (using step_by)
for i in (0..=10).step_by(2) {
println!("Even: {}", i); // 0, 2, 4, 6, 8, 10
}
// Using variables in ranges
let start = 5;
let end = 10;
for i in start..=end {
println!("Variable range: {}", i);
}
}
For Loops with Collections
fn main() {
// Arrays
let arr = [10, 20, 30, 40, 50];
for element in arr.iter() {
println!("Array element: {}", element);
}
// Vectors
let vec = vec!["apple", "banana", "cherry"];
for fruit in &vec {
println!("Fruit: {}", fruit);
}
// With index using enumerate
for (index, fruit) in vec.iter().enumerate() {
println!("Fruit {}: {}", index, fruit);
}
// Mutable iteration
let mut numbers = vec![1, 2, 3, 4, 5];
for num in &mut numbers {
*num *= 2;
}
println!("Doubled: {:?}", numbers);
// HashMaps
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert("Alice", 100);
scores.insert("Bob", 90);
scores.insert("Charlie", 95);
for (name, score) in &scores {
println!("{}: {}", name, score);
}
}
For Loops with Custom Iterators
struct Counter {
count: u32,
max: u32,
}
impl Counter {
fn new(max: u32) -> Counter {
Counter { count: 0, max }
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count <= self.max {
Some(self.count)
} else {
None
}
}
}
fn main() {
// Using custom iterator
let counter = Counter::new(5);
for number in counter {
println!("Count: {}", number);
}
// Chaining iterator methods
let sum: u32 = Counter::new(10)
.filter(|&x| x % 2 == 0)
.map(|x| x * x)
.sum();
println!("Sum of squares of evens: {}", sum);
}
5. Loop Control Statements
Break Statement
fn main() {
// Basic break
for i in 1..=10 {
if i == 5 {
println!("Breaking at {}", i);
break;
}
println!("i = {}", i);
}
// Break with value in loop
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("Break with value: {}", result);
// Break in nested loops with labels
'outer: for i in 1..=3 {
for j in 1..=3 {
if i == 2 && j == 2 {
println!("Breaking outer at ({}, {})", i, j);
break 'outer;
}
println!("({}, {})", i, j);
}
}
}
Continue Statement
fn main() {
// Basic continue
for i in 1..=10 {
if i % 2 == 0 {
continue; // Skip even numbers
}
println!("Odd: {}", i);
}
// Continue with label
'outer: for i in 1..=5 {
for j in 1..=5 {
if i == j {
println!("Skipping diagonal at ({}, {})", i, j);
continue 'outer; // Continue outer loop
}
println!("({}, {})", i, j);
}
}
// Practical example: process only valid items
let items = vec![1, -2, 3, -4, 5, -6];
for item in items {
if item < 0 {
println!("Skipping negative: {}", item);
continue;
}
println!("Processing positive: {}", item);
}
}
Combining Break and Continue
fn main() {
// Complex flow with break and continue
let data = vec![
vec![1, 2, -1, 4],
vec![5, -2, 7, 8],
vec![9, 10, 11, -3],
];
'outer: for (row_idx, row) in data.iter().enumerate() {
println!("Processing row {}", row_idx);
for (col_idx, &value) in row.iter().enumerate() {
if value < 0 {
println!("Found negative at ({}, {}), skipping row", row_idx, col_idx);
continue 'outer;
}
if value == 10 {
println!("Found 10 at ({}, {}), stopping completely", row_idx, col_idx);
break 'outer;
}
println!(" Value at ({}, {}): {}", row_idx, col_idx, value);
}
}
}
6. The while let Construct
Pattern Matching with While Let
fn main() {
// Basic while let with Option
let mut optional = Some(0);
while let Some(x) = optional {
if x > 5 {
println!("Reached {}, stopping", x);
optional = None;
} else {
println!("x is {}", x);
optional = Some(x + 1);
}
}
// With Result
let mut results: Vec<Result<i32, &str>> = vec![
Ok(1),
Ok(2),
Err("error"),
Ok(3),
];
while let Some(Ok(value)) = results.pop() {
println!("Got value: {}", value);
}
// With custom enum
enum MyEnum {
Foo(i32),
Bar(String),
Baz,
}
let mut values = vec![
MyEnum::Foo(42),
MyEnum::Bar("hello".to_string()),
MyEnum::Baz,
MyEnum::Foo(100),
];
while let Some(MyEnum::Foo(x)) = values.pop() {
println!("Found Foo: {}", x);
}
}
While Let with References
fn main() {
// Working with references
let mut data = vec![Some(1), Some(2), None, Some(3)];
while let Some(Some(value)) = data.pop() {
println!("Popped value: {}", value);
}
// With mutable references
let mut numbers = vec![1, 2, 3, 4, 5];
let mut iter = numbers.iter_mut();
while let Some(num) = iter.next() {
*num *= 2;
}
println!("Doubled: {:?}", numbers);
// Destructuring in while let
let mut pairs = vec![(1, "one"), (2, "two"), (3, "three")];
while let Some((num, name)) = pairs.pop() {
println!("{}: {}", name, num);
}
}
7. Loop Performance Considerations
Efficient Looping
fn main() {
// Avoid unnecessary allocations
let data = vec![1, 2, 3, 4, 5];
// Good: Iterate by reference
for item in &data {
println!("{}", item);
}
// Bad: Would consume the vector
// for item in data { // data moved here
// println!("{}", item);
// }
// Still can use data
println!("Data still available: {:?}", data);
// Pre-allocate when size known
let mut results = Vec::with_capacity(1000);
for i in 0..1000 {
results.push(i * 2);
}
println!("Results length: {}", results.len());
}
Loop Optimizations
fn main() {
// Compiler optimizations
// Simple loops are often optimized
// This loop might be unrolled or vectorized
let mut sum = 0;
for i in 0..1000 {
sum += i;
}
println!("Sum: {}", sum);
// Using iterators can be as fast as manual loops
let data: Vec<i32> = (0..1000).collect();
// Iterator version (often optimized well)
let sum_iter: i32 = data.iter().sum();
println!("Iterator sum: {}", sum_iter);
// Manual loop version
let mut sum_manual = 0;
for i in 0..data.len() {
sum_manual += data[i];
}
println!("Manual sum: {}", sum_manual);
}
8. Common Loop Patterns
Accumulation Patterns
fn main() {
// Sum accumulation
let numbers = [1, 2, 3, 4, 5];
let mut sum = 0;
for num in numbers.iter() {
sum += num;
}
println!("Sum: {}", sum);
// Product accumulation
let mut product = 1;
for num in 1..=5 {
product *= num;
}
println!("Factorial of 5: {}", product);
// Building collections
let mut squares = Vec::new();
for i in 1..=10 {
squares.push(i * i);
}
println!("Squares: {:?}", squares);
// Finding max/min
let values = [45, 23, 78, 12, 56, 89, 34];
let mut max = values[0];
for &value in values.iter().skip(1) {
if value > max {
max = value;
}
}
println!("Maximum: {}", max);
}
Filtering Patterns
fn main() {
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Filter even numbers
let mut evens = Vec::new();
for &num in numbers.iter() {
if num % 2 == 0 {
evens.push(num);
}
}
println!("Evens: {:?}", evens);
// Filter and transform
let mut doubled_odds = Vec::new();
for &num in numbers.iter() {
if num % 2 != 0 {
doubled_odds.push(num * 2);
}
}
println!("Doubled odds: {:?}", doubled_odds);
// Counting matches
let mut count = 0;
for &num in numbers.iter() {
if num > 5 {
count += 1;
}
}
println!("Numbers > 5: {}", count);
}
Searching Patterns
fn main() {
let data = [4, 8, 15, 16, 23, 42];
// Linear search
let target = 23;
let mut found_index = None;
for (i, &value) in data.iter().enumerate() {
if value == target {
found_index = Some(i);
break;
}
}
match found_index {
Some(i) => println!("Found {} at index {}", target, i),
None => println!("{} not found", target),
}
// Search with condition
let threshold = 20;
let mut found_value = None;
for &value in data.iter() {
if value > threshold {
found_value = Some(value);
break;
}
}
println!("First value > {}: {:?}", threshold, found_value);
}
9. Infinite Loops and Termination
Safe Infinite Loops
use std::thread;
use std::time::Duration;
fn main() {
// Server-like infinite loop
let mut request_count = 0;
loop {
request_count += 1;
println!("Processing request #{}", request_count);
// Simulate work
thread::sleep(Duration::from_millis(100));
// Condition to break (in real server, this might never happen)
if request_count >= 10 {
println!("Shutting down after {} requests", request_count);
break;
}
}
// Event loop pattern
let mut events = vec!["click", "keypress", "resize", "close"];
while let Some(event) = events.pop() {
println!("Handling event: {}", event);
if event == "close" {
println!("Close event received, exiting event loop");
break;
}
}
// Polling loop
let mut attempts = 0;
let max_attempts = 5;
loop {
attempts += 1;
println!("Polling attempt {}", attempts);
// Simulate checking a condition
let ready = attempts >= 3;
if ready {
println!("Resource ready!");
break;
}
if attempts >= max_attempts {
println!("Timeout: resource not ready");
break;
}
thread::sleep(Duration::from_millis(500));
}
}
Preventing Infinite Loops
fn main() {
// Always ensure loops have a termination condition
let mut counter = 0;
let max_iterations = 1000;
while some_condition() {
counter += 1;
// Safety check
if counter > max_iterations {
println!("Loop exceeded max iterations, breaking");
break;
}
// Normal loop logic
println!("Iteration {}", counter);
}
// Using iterators instead of manual loops when possible
let numbers = 0..;
// This is safe because we limit with take()
for num in numbers.take(10) {
println!("Number: {}", num);
}
}
fn some_condition() -> bool {
// Simulate condition that might always be true
true
}
10. Advanced Loop Techniques
Loop with State
fn main() {
// State machine in a loop
#[derive(Debug, PartialEq)]
enum State {
Start,
Processing,
Validating,
Completed,
Error,
}
let mut state = State::Start;
let mut data = 0;
loop {
match state {
State::Start => {
println!("Starting process...");
data = 42;
state = State::Processing;
}
State::Processing => {
println!("Processing data: {}", data);
data *= 2;
state = State::Validating;
}
State::Validating => {
println!("Validating result: {}", data);
if data > 0 {
state = State::Completed;
} else {
state = State::Error;
}
}
State::Completed => {
println!("Process completed with result: {}", data);
break;
}
State::Error => {
println!("Error occurred!");
break;
}
}
}
}
Generator Pattern
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)
}
}
fn main() {
// Using generator pattern
let fib = Fibonacci::new();
for (i, num) in fib.take(10).enumerate() {
println!("Fibonacci {}: {}", i + 1, num);
}
// Custom range generator
struct StepRange {
current: i32,
end: i32,
step: i32,
}
impl StepRange {
fn new(start: i32, end: i32, step: i32) -> Self {
StepRange {
current: start,
end,
step,
}
}
}
impl Iterator for StepRange {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current <= self.end {
let result = self.current;
self.current += self.step;
Some(result)
} else {
None
}
}
}
let range = StepRange::new(0, 10, 2);
for num in range {
println!("Step: {}", num);
}
}
Parallel Processing Patterns
use std::thread;
fn main() {
// Simple parallel processing with threads
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let chunk_size = data.len() / 4;
let mut handles = vec![];
for chunk in data.chunks(chunk_size) {
let chunk = chunk.to_vec();
handles.push(thread::spawn(move || {
let sum: i32 = chunk.iter().sum();
println!("Chunk sum: {}", sum);
sum
}));
}
let mut total = 0;
for handle in handles {
total += handle.join().unwrap();
}
println!("Total sum: {}", total);
// Rayon parallel iterators (if feature enabled)
// use rayon::prelude::*;
// let sum: i32 = data.par_iter().sum();
// println!("Parallel sum: {}", sum);
}
11. Error Handling in Loops
Loops with Results
use std::fs::File;
use std::io::{self, Write};
fn main() -> io::Result<()> {
// Loop with Result handling
let mut attempts = 0;
let max_attempts = 3;
let result = loop {
attempts += 1;
match try_operation() {
Ok(value) => break Ok(value),
Err(e) => {
println!("Attempt {} failed: {}", attempts, e);
if attempts >= max_attempts {
break Err(e);
}
}
}
};
match result {
Ok(value) => println!("Success: {}", value),
Err(e) => println!("All attempts failed: {}", e),
}
// Collecting Results
let operations = vec!["file1.txt", "file2.txt", "file3.txt"];
let mut successful = Vec::new();
let mut errors = Vec::new();
for filename in operations {
match File::create(filename) {
Ok(file) => successful.push(file),
Err(e) => errors.push((filename, e)),
}
}
println!("Successful: {}, Failed: {}", successful.len(), errors.len());
Ok(())
}
fn try_operation() -> Result<i32, &'static str> {
// Simulate operation that might fail
static COUNTER: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
let attempt = COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
if attempt < 2 {
Err("Temporary failure")
} else {
Ok(42)
}
}
Loop with Option
fn main() {
// Processing until None
let mut data = Some(0);
while let Some(x) = data {
println!("Processing: {}", x);
if x >= 5 {
data = None;
} else {
data = Some(x + 1);
}
}
// Collecting Options
let items = vec![Some(1), None, Some(2), Some(3), None, Some(4)];
let mut valid_items = Vec::new();
for item in items {
if let Some(value) = item {
valid_items.push(value);
} else {
println!("Skipping None");
continue;
}
}
println!("Valid items: {:?}", valid_items);
// Find first Some
let values = vec![None, None, Some(42), None, Some(100)];
let mut found = None;
for value in values {
if value.is_some() {
found = value;
break;
}
}
println!("First Some: {:?}", found);
}
12. Loop Macros and Utilities
Custom Loop Macros
macro_rules! repeat {
($n:expr, $body:block) => {
for _ in 0..$n {
$body
}
};
}
macro_rules! while_not {
($cond:expr, $body:block) => {
while !$cond {
$body
}
};
}
macro_rules! loop_with_index {
($iter:expr, $body:expr) => {
for (index, item) in $iter.enumerate() {
$body(index, item);
}
};
}
fn main() {
// Using custom macros
repeat!(5, {
println!("Repeating!");
});
let mut x = 0;
while_not!(x >= 5, {
println!("x = {}", x);
x += 1;
});
let data = vec!["a", "b", "c", "d"];
loop_with_index!(data.iter(), |i, v| {
println!("Index {}: {}", i, v);
});
}
Utility Functions for Loops
// Generic loop utilities
fn times<F>(n: usize, mut f: F)
where
F: FnMut(usize),
{
for i in 0..n {
f(i);
}
}
fn repeat_until<T, F>(mut f: F) -> T
where
F: FnMut() -> Option<T>,
{
loop {
if let Some(result) = f() {
return result;
}
}
}
fn for_each_with_state<T, S, F>(items: &[T], mut state: S, mut f: F)
where
F: FnMut(&T, &mut S),
{
for item in items {
f(item, &mut state);
}
}
fn main() {
// Using utility functions
times(5, |i| {
println!("Time {}: Hello!", i);
});
let mut counter = 0;
let result = repeat_until(|| {
counter += 1;
println!("Attempt {}", counter);
if counter >= 5 {
Some(counter)
} else {
None
}
});
println!("Result after {} attempts", result);
let numbers = vec![1, 2, 3, 4, 5];
let mut sum = 0;
for_each_with_state(&numbers, &mut sum, |&num, acc| {
**acc += num;
});
println!("Sum: {}", sum);
}
13. Best Practices and Common Patterns
Idiomatic Loop Usage
fn main() {
// Prefer for loops over while for iterating collections
let items = vec![1, 2, 3, 4, 5];
// Good
for item in &items {
println!("{}", item);
}
// Less idiomatic
let mut i = 0;
while i < items.len() {
println!("{}", items[i]);
i += 1;
}
// Use iterators for transformations
let squares: Vec<i32> = items.iter().map(|&x| x * x).collect();
println!("Squares: {:?}", squares);
// Use enumerate when index is needed
for (i, item) in items.iter().enumerate() {
println!("items[{}] = {}", i, item);
}
// Use zip for parallel iteration
let names = vec!["Alice", "Bob", "Charlie"];
let ages = vec![30, 25, 35];
for (name, age) in names.iter().zip(ages.iter()) {
println!("{} is {} years old", name, age);
}
}
Loop Performance Tips
fn main() {
// Avoid unnecessary bounds checks
let data = vec![1, 2, 3, 4, 5];
// Good: Iterator avoids bounds checks
for &x in &data {
println!("{}", x);
}
// Less efficient: Manual indexing with bounds checks
for i in 0..data.len() {
println!("{}", data[i]); // Bounds check each iteration
}
// Pre-allocate when size known
let mut results = Vec::with_capacity(1000);
for i in 0..1000 {
results.push(i * 2);
}
// Use references to avoid copying
let large_data = vec![String::from("hello"); 100];
for s in &large_data { // Borrows, doesn't copy
println!("{}", s);
}
// For large data, iterating by value consumes
// for s in large_data { // large_data moved here
// println!("{}", s);
// }
// Can't use large_data after this
}
Readability Patterns
fn main() {
// Use meaningful variable names
let mut total = 0;
// Good
for student_score in &[85, 92, 78, 95, 88] {
total += student_score;
}
// Use early continue for clarity
for number in 1..=20 {
// Skip odd numbers early
if number % 2 != 0 {
continue;
}
// Process even numbers
println!("Even: {}", number);
}
// Complex condition with named boolean
let should_process = true;
let data = vec![1, 2, 3, 4, 5];
for value in data {
if !should_process {
break;
}
// Complex processing here
println!("Processing: {}", value);
}
// Using loop labels for clarity
'find_first: for i in 0..10 {
for j in 0..10 {
if i * j == 42 {
println!("Found at ({}, {})", i, j);
break 'find_first;
}
}
}
}
Conclusion
Rust's loop system provides powerful and safe iteration constructs:
Key Takeaways
- loop: Infinite loops with break/continue control
- while: Condition-based looping
- for: Iterator-based looping (most common)
- while let: Pattern matching loops
- Loop Labels: Named loops for nested control
- Break with Values: Return values from loops
- Iterator Integration: Seamless work with iterators
Best Practices
- Prefer
forloops with iterators for collections - Use
while letfor pattern-based looping - Use loop labels for clarity in nested loops
- Break with values when you need a result
- Consider performance with large datasets
- Use meaningful names for loop variables
- Handle errors appropriately in loops
When to Use Each Loop Type
| Loop Type | Best Used For |
|---|---|
loop | Infinite loops, retry logic, servers |
while | Condition-based iteration |
for | Collection iteration, ranges |
while let | Pattern matching, Option/Result unwrapping |
Rust's loop constructs combine safety with expressiveness, making it easy to write correct and efficient iteration code.