loop
loops
Loops defined with loop
run forever until you explicitly tell them to stop with the break
keyword. If you want to return a value from your loop, add this after the break
keyword. You might want to use this kind of loop to retry things which might fail, for example waiting for a thread to complete its job.
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
// 20
}
If you have lots of nested loops, you can give them labels, and then use these labels to target specific loops with break
or continue
.
fn main() {
let mut count = 0;
'counting_up: loop { // label this loop "counting_up"
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break; // break out of this inner loop
}
if count == 2 {
break 'counting_up; // break out of the "counting_up" loop
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
// 2
}
while
loops
Declare loops with while
to loop if a condition is true, else break out of it. You could do this by hand with loop
, conditionals and manual breaks, but this syntax is often cleaner
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
for
loops
Use for
loops to execute some code for each item in a collection.
fn main() {
let a = [10, 20, 30, 40, 50]; // this is an array remember
for element in a {
println!("the value is: {element}");
}
}
Sure you could do this with a while
, but you are much more likely to mess up. for
is probably the most common type of loop.
while let
loops also exist
which you might want to use when doing something over a collection that might be a nasty nested match statement otherwise.
fn main() {
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ Less rightward drift than match, and doesn't
// require explicitly handling the failing case.
}
}
Usually you want a for-loop for iterating over a collection, but you’d use this thing instead when the thing you are iterating over needs something async doing to it, for example reading lines from a file where the value needs to be awaited.
use tokio::fs::File;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Result as TokioResult};
#[tokio::main]
async fn main() -> TokioResult<()> {
create_file().await?;
let file = File::open("/tmp/test.txt").await?;
// Shadow file here because I don't really care there is a bufreader thingy,
// I just want the file contents
let mut file = BufReader::new(file).lines();
// You have to call lines before your loop so you have an iterator thingy, not a BufReader,
// because the loop needs to access the thing every time it runs and ownership things
while let Some(line) = file.next_line().await? {
println!("{line}");
}
// You can't do this because for is trying to do iterator.next(),
// but Lines (from tokio) isn't an iterator (because it does async things it's a stream)
// for line in file {
// println!("{line}");
// }
Ok(())
// prints hello then world, because that's whats in your file until it runs out of lines
}
async fn create_file() -> tokio::io::Result<()> {
File::create("/tmp/test.txt")
.await?
.write_all(b"hello\nworld")
.await
}