Rust Programming: Control Flow - Loops
Rust provides several ways to execute code repeatedly, known as loops. These are essential for automating tasks and processing collections of data. Here's a breakdown of the different loop types in Rust:
1. loop - The Infinite Loop
The loop keyword creates an infinite loop. It continues executing indefinitely unless explicitly broken with a break statement.
fn main() {
let mut counter = 0;
loop {
counter += 1;
println!("Counter: {}", counter);
if counter == 5 {
break; // Exit the loop when counter reaches 5
}
}
println!("Loop finished!");
}
Explanation:
loop { ... }: Defines the infinite loop.counter += 1;: Increments thecountervariable in each iteration.if counter == 5 { break; }: This is the crucial part. Thebreakstatement exits theloopwhencounterequals 5. Withoutbreak, the loop would run forever.println!("Loop finished!");: This line executes after the loop has terminated.
break with a value:
You can also use break to return a value from a loop. This is particularly useful when the loop is nested within a function.
fn find_first_positive(numbers: &[i32]) -> Option<i32> {
for &num in numbers {
if num > 0 {
return Some(num); // Break and return Some(num)
}
}
None // Return None if no positive number is found
}
fn main() {
let numbers = [-2, -1, 0, 1, 2, -3];
let first_positive = find_first_positive(&numbers);
match first_positive {
Some(num) => println!("First positive number: {}", num),
None => println!("No positive number found."),
}
}
2. while - Conditional Loop
The while loop executes a block of code as long as a specified condition is true.
fn main() {
let mut number = 3;
while number != 0 {
println!("Number: {}", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
Explanation:
while number != 0 { ... }: The loop continues as long asnumberis not equal to 0.number -= 1;: Decrementsnumberin each iteration. This is essential to eventually make the conditionnumber != 0false, otherwise the loop will be infinite.
3. for - Iterating over Collections
The for loop is used to iterate over a sequence of items, such as arrays, vectors, ranges, and other iterable data structures.
a) Iterating over a Range:
fn main() {
for number in 1..4 { // 1, 2, 3 (exclusive of 4)
println!("Number: {}", number);
}
println!("Finished!");
}
Explanation:
for number in 1..4 { ... }: Iterates over the range of numbers from 1 (inclusive) to 4 (exclusive). The..operator creates a range.1..=4would include 4 in the iteration.
b) Iterating over a Vector:
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("The value is: {}", element);
}
}
Explanation:
a.iter(): Creates an iterator over the elements of the arraya. Iterators are a fundamental concept in Rust for working with collections.for element in a.iter() { ... }: Iterates over each element in the iterator.
c) Iterating with Mutable Access:
If you need to modify the elements of a collection while iterating, use a.iter_mut().
fn main() {
let mut a = [10, 20, 30, 40, 50];
for element in a.iter_mut() {
*element += 5; // Dereference the element to modify its value
}
println!("Modified array: {:?}", a); // Output: [15, 25, 35, 45, 55]
}
Explanation:
a.iter_mut(): Creates a mutable iterator over the elements of the arraya.*element += 5;: The*operator dereferences theelement(which is a reference to an element in the array), allowing you to modify the actual value in the array.
d) Iterating with Ownership (Consuming the Collection):
If you want to take ownership of the elements during iteration, use a.into_iter(). This consumes the original collection.
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.into_iter() {
println!("The value is: {}", element);
}
// a is no longer valid here because ownership was moved into the loop.
// println!("{:?}", a); // This would cause a compile error.
}
4. Control Flow within Loops
You can use continue and break statements within loops to control their execution:
continue: Skips the rest of the current iteration and proceeds to the next iteration.
fn main() {
for number in 1..10 {
if number % 2 == 0 {
continue; // Skip even numbers
}
println!("Odd number: {}", number);
}
}
break: Exits the loop entirely. (As shown in theloopexample above).
5. Loop Labels
You can label loops to control which loop break or continue affects when you have nested loops.
fn main() {
'outer: for x in 1..=3 {
for y in 1..=3 {
println!("x: {}, y: {}", x, y);
if x == 2 && y == 2 {
break 'outer; // Break the outer loop
}
}
}
println!("Finished!");
}
Explanation:
'outer:: This labels the outer loop.break 'outer;: This breaks the loop labeled'outer. Without the label, it would only break the inner loop.
Key Takeaways:
loop: Infinite loop, requiresbreakto exit.while: Conditional loop, executes as long as a condition is true.for: Iterates over a sequence of items. Useiter(),iter_mut(), orinto_iter()depending on your needs.continue: Skips the current iteration.break: Exits the loop.- Loop Labels: Useful for controlling
breakandcontinuein nested loops.
Understanding these loop types and control flow statements is crucial for writing effective and efficient Rust programs. Practice using them to become comfortable with their behavior.