Loops
Syntax#
-
loop { block } // infinite loop
-
while condition { block }
-
while let pattern = expr { block }
-
for pattern in expr { block } // expr must implement IntoIterator
-
continue // jump to the end of the loop body, starting a new iteration if necessary
-
break // stop the loop
-
’label: loop { block }
-
’label: while condition { block }
-
’label: while let pattern = expr { block }
-
’label: for pattern in expr { block }
-
continue ’label // jump to the end of the loop body labelled label, starting a new iteration if necessary
-
break ’label // stop the loop labelled label
Basics
There are 4 looping constructs in Rust. All examples below produce the same output.
Infinite Loops
let mut x = 0;
loop {
if x > 3 { break; }
println!("{}", x);
x += 1;
}
While Loops
let mut x = 0;
while x <= 3 {
println!("{}", x);
x += 1;
}
Also see: What is the difference between loop
and while true
?
Pattern-matched While Loops
These are sometimes known as while let
loops for brevity.
let mut x = Some(0);
while let Some(v) = x {
println!("{}", v);
x = if v < 3 { Some(v + 1) }
else { None };
}
This is equivalent to a match
inside a loop
block:
let mut x = Some(0);
loop {
match x {
Some(v) => {
println!("{}", v);
x = if v < 3 { Some(v + 1) }
else { None };
}
_ => break,
}
}
For Loops
In Rust, for
loop can only be used with an “iterable” object (i.e. it should implement IntoIterator
).
for x in 0..4 {
println!("{}", x);
}
This is equivalent to the following snippet involving while let
:
let mut iter = (0..4).into_iter();
while let Some(v) = iter.next() {
println!("{}", v);
}
Note: 0..4
returns a Range
object which already implements the Iterator
trait. Therefore into_iter()
is unnecessary, but is kept just to illustrate what for
does. For an in-depth look, see the official docs on for
Loops and IntoIterator
.
Also see: https://stackoverflow.com/documentation/rust/4657/iterators
More About For Loops
As mentioned in Basics, we can use anything which implements IntoIterator
with the for
loop:
let vector = vec!["foo", "bar", "baz"]; // vectors implement IntoIterator
for val in vector {
println!("{}", val);
}
Expected output:
foo
bar
baz
Note that iterating over vector
in this way consumes it (after the for
loop, vector
can not be used again). This is because IntoIterator::into_iter
moves self
.
IntoIterator
is also implemented by &Vec<T>
and &mut Vec<T>
(yielding values with types &T
and &mut T
respectively) so you can prevent the move of vector
by simply passing it by reference:
let vector = vec!["foo", "bar", "baz"];
for val in &vector {
println!("{}", val);
}
println!("{:?}", vector);
Note that val
is of type &&str
, since vector
is of type Vec<&str>
.
Loop Control
All looping constructs allow the use of break
and continue
statements. They affect the immediately surrounding (innermost) loop.
Basic Loop Control
break
terminates the loop:
for x in 0..5 {
if x > 2 { break; }
println!("{}", x);
}
Output
0
1
2
continue
finishes the current iteration early
for x in 0..5 {
if x < 2 { continue; }
println!("{}", x);
}
Output
2
3
4
Advanced Loop Control
Now, suppose we have nested loops and want to break
out to the outer loop. Then, we can use loop labels to specify which loop a break
or continue
applies to. In the following example, 'outer
is the label given to the outer loop.
'outer: for i in 0..4 {
for j in i..i+2 {
println!("{} {}", i, j);
if i > 1 {
continue 'outer;
}
}
println!("--");
}
Output
0 0
0 1
--
1 1
1 2
--
2 2
3 3
For i > 1
, the inner loop was iterated only once and --
was not printed.
Note: Do not confuse a loop label with a lifetime variable. Lifetime variables only occurs beside an &
or as a generic parameter within <>
.