Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
JOHNWICK
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Loops
Page
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
Loops in programming (fundamental and very useful), including Rust, are like a repeating task you tell the computer to do until a certain condition is met or a task is finished. Think of loops as asking someone to keep stirring a pot of soup until itâs ready. Yesterday we covered iterators, and if you missed that you can check it out below. Iterators After playing with vectors, and enums the last two days, itâs time to look in on Rustâs iterators -a feature that makes⌠medium.com Rust provides three main loop constructs: loop, while, and for. Each with its own strengths, and useful for different situations. Loops can be useful whether youâre parsing logs, iterating over database records, or retrying network requests. In this writeup we will look at Loops in Rust in-depth. The loop Construct The loop keyword creates an infinite loop that runs until you explicitly stop it with break. This makes loops perfect for scenarios where you need to keep trying something until a condition is met, like polling a server or waiting for user input. Imagine weâre building a command-line tool that monitors a serverâs status by sending HTTP requests until it gets a successful response. Below is how we might use loop to handle this: <pre> use std::time::Duration; use std::thread; fn check_server_status() -> bool { // Simulate a server check; returns true if server is up // In a real app, this might be an HTTP request rand::random::<bool>() } fn main() { let mut attempts = 0; let max_attempts = 5; loop { if attempts >= max_attempts { println!("Failed to connect after {} attempts.", max_attempts); break; } if check_server_status() { println!("Server is up!"); break; } println!("Attempt {} failed. Retrying...", attempts + 1); attempts += 1; thread::sleep(Duration::from_secs(1)); } } </pre> Take a look at the code above, the loop keeps trying to connect to the server until it either succeeds or hits the maximum number of attempts. The break statement is your escape hatch, it lets you exit cleanly when the job is done or when youâve tried too many times. It is a common pattern in system utilities or monitoring tools where you need to retry operations. You can also use loop to return a value by adding an expression after break. Suppose youâre parsing a log file line by line until you find a specific error code, and you want to return the line number where it was found: <pre> fn find_error_line(logs: &[&str], error_code: &str) -> Option<usize> { let mut line_number = 0; loop { if line_number >= logs.len() { return None; } if logs[line_number].contains(error_code) { break Some(line_number); } line_number += 1; } } fn main() { let logs = vec!["INFO: System started", "ERROR: 404 Not Found", "INFO: System shutdown"]; match find_error_line(&logs, "404") { Some(line) => println!("Error found at line {}", line + 1), None => println!("No error found"), } } </pre> The code above loop searches through a vector of log entries and returns the index of the first line containing the error code, or None if itâs not found. Itâs a pattern you might find useful when analyzing log files or processing data streams in a backend service. Now that we have looked at loop, letâs look at while loop. The while Loop The while loop runs as long as a condition is true, making it ideal for situations where you know when to stop based on some state. Youâll often see while in scenarios where youâre processing data until a resource is exhausted or a condition changes. Consider a scenario where youâre reading messages from a queue in a messaging system, stopping when the queue is empty or an error occurs: <pre> struct MessageQueue { messages: Vec<String>, } impl MessageQueue { fn pop(&mut self) -> Option<String> { self.messages.pop() } } fn process_messages(queue: &mut MessageQueue) { while let Some(message) = queue.pop() { println!("Processing message: {}", message); // we could Simulate processing here; // could be saving to a database or // sending to another service } println!("Queue is empty!"); } fn main() { let mut queue = MessageQueue { messages: vec![ String::from("User logged in"), String::from("User updated profile"), String::from("User logged out"), ], }; process_messages(&mut queue); } </pre> In our snippet above, while let is used to keep processing messages until the queue is empty. The pop method returns None when there are no more messages, which stops the loop. This pattern is common in systems that handle event streams, like message brokers or task queues in a microservices architecture. Another real-world use case is validating user input. Suppose youâre writing a CLI tool that asks for a valid email address: <pre> use std::io::{self, Write}; fn is_valid_email(email: &str) -> bool { email.contains('@') // Simplified validation } fn get_valid_email() -> String { let mut email = String::new(); while !is_valid_email(&email) { print!("Enter a valid email address: "); io::stdout().flush().unwrap(); email.clear(); io::stdin().read_line(&mut email).unwrap(); email = email.trim().to_string(); } email } fn main() { let email = get_valid_email(); println!("Valid email entered: {}", email); } </pre> The while loop keeps prompting until the user provides a valid email. This is a typical pattern in interactive applications where you need to ensure input meets certain criteria before proceeding. The for Loop The for loop is your go-to for iterating over collections, like arrays, vectors, or ranges. Itâs clean and expressive, especially when paired with Rustâs iterators, which let you process data in a functional style. Youâll use for loops when you need to process every item in a dataset, like generating reports or transforming data. Letâs say youâre working on a data processing pipeline that calculates the total sales from a list of transactions: <pre> struct Transaction { amount: f64, customer_id: u32, } fn calculate_total_sales(transactions: &[Transaction]) -> f64 { let mut total = 0.0; for transaction in transactions { total += transaction.amount; } total } fn main() { let transactions = vec![ Transaction { amount: 100.50, customer_id: 1 }, Transaction { amount: 200.75, customer_id: 2 }, Transaction { amount: 50.25, customer_id: 1 }, ]; let total = calculate_total_sales(&transactions); println!("Total sales: ${:.2}", total); } </pre> In our code above, the for loop iterates over a slice of Transaction structs, summing their amounts. This is a common task in financial applications or e-commerce platforms where you need to aggregate data. Rustâs for loop also shines with ranges, which are handy for tasks like generating sequences or retrying operations a fixed number of times. Suppose youâre writing a load tester that sends a fixed number of requests to a server: <pre> fn send_request(id: u32) -> bool { // we could simulate a request; returns true if successful println!("Sending request {}", id); true } fn main() { for i in 1..=5 { if send_request(i) { println!("Request {} succeeded", i); } else { println!("Request {} failed", i); } } } </pre> The 1..=5 range includes numbers from 1 to 5, making the loop iterate exactly five times. This is useful in testing tools or scripts where you need to perform a task a specific number of times. Choosing the Right Loop Each loop type has its own strengths, and useful for different situations. * Use loop for infinite or unpredictable tasks, like retrying network calls or polling. * Use while when you have a condition that determines when to stop, like processing a queue or validating input. * Use for for iterating over collections or fixed ranges, like processing data or generating sequences. In real world scenarios, youâll often combine loops with Rustâs ownership and borrowing rules to ensure memory safety. For example, when iterating over a vector, you might borrow it as a slice (&vec) to avoid moving it, or use &mut if you need to modify elements (we covered this on vectors yesterday). Loops with Iterators Rustâs iterators (as we saw yesterday) can supercharge your for loops, letting you chain operations like filtering or mapping. Imagine youâre filtering out invalid transactions from a dataset before summing them: <pre> fn calculate_valid_sales(transactions: &[Transaction]) -> f64 { transactions .iter() .filter(|t| t.amount > 0.0) .map(|t| t.amount) .sum() } fn main() { let transactions = vec![ Transaction { amount: 100.50, customer_id: 1 }, Transaction { amount: -10.0, customer_id: 2 }, // Invalid Transaction { amount: 50.25, customer_id: 1 }, ]; let total = calculate_valid_sales(&transactions); println!("Total valid sales: ${:.2}", total); } </pre> The code above uses a for-like iterator chain to filter out negative amounts and sum the rest, all in a concise, readable way. Youâll see this pattern in data processing pipelines or analytics tools where you need to transform and aggregate data efficiently. Common Pitfalls and Tips Below are some common pitfalls to look out for and avoid while working with loops in Rust. * Be cautious with loop to avoid infinite loops. Always ensure thereâs a break condition, like a counter or a timeout. * When using for with mutable data, ensure youâre borrowing correctly. Use & for read-only access or &mut for modifications. * When chaining iterators, remember that methods like collect consume the iterator, so plan your data flow to avoid moving data unexpectedly. * For large datasets, prefer iterators over manual indexing in while loops to take advantage of Rustâs optimizations. Read the full article here: https://medium.com/rustaceans/loops-e2026244a069
Summary:
Please note that all contributions to JOHNWICK may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
JOHNWICK:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Search
Search
Editing
Loops
Add topic