Control Flow
As in other imperative languages, execution of a Rust program starts in the main function and proceeds sequentially from statement to statement. This linear flow can be made to branch or cycle back using conditional statements and loops.
If Expressions
Rust's if structure may be used as either a statement or an expression. This if statement categorizes a wavelength of visible light—measured in nanometers—into one of three broad colors:
if wavelength < 520 {
println!("bluish");
} else if wavelength < 590 {
println!("greenish");
} else {
println!("reddish");
}
This form is very much like the conditional statements of C and Java. The primary difference is that conditions in Rust are not surrounded by parentheses, as in Ruby. The Rust compiler warns us against using them.
If Rust doesn't require parentheses, why are they required in C and Java? In those languages, curly braces around then- and else-blocks and loop blocks are optional if the blocks consist of a single statement. Without parentheses, conditions and unbraced single statements would run together and make parsing difficult. In Rust, braces are always required around a block, which means parentheses are never needed to separate the condition from the then block.
We could factor out the repeated println!
calls from the conditional above by using an if expression that yields the color:
let color = if wavelength < 520 {
"bluish"
} else if wavelength < 590 {
"greenish"
} else {
"reddish"
};
println!("{}", color);
When we have a conditional expression, a semi-colon is needed after the final branch to end the statement in which the expression appears. The expressions of the branches that produce the branch's value do not end in semi-colons.
While Statements
Rust's while loop behaves much like the while loops we have seen in other languages. Here a while loop is used to print the first 12 powers-of-two using bit-shifting:
let mut i = 0;
while i < 12 {
println!("{}", 1 << i);
i += 1;
}
The while loop is purely a statement, not an expression.
For-in Statements
The only for loop in Rust is the for-in loop that iterates through a collection or range. This for loop is equivalent to the while loop above:
for i in 0..12 {
println!("{}", 1 << i);
}
Iterator i
is not declared with let
or mut
. It exists in the loop's scope, but not in the surrounding scope. We can't access i
after the loop finishes.
This for loop iterates through an array of integers:
let xs = [1, 11, 21, 1211, 111221];
for x in xs {
println!("{}", x);
}
The Rust compiler implicitly inserts a call to get an iterator for the collection through which we are looping. The transformed loop looks like this:
let xs = [1, 11, 21, 1211, 111221];
for x in xs.iter() {
println!("{}", x);
}
If we need an index, we can explicitly enhance the iterator with enumerate()
. Each iteration then yields a tuple of the current index and value. The tuple may be destructured directly in the loop header:
let xs = [1, 11, 21, 1211, 111221];
for (index, x) in xs.iter().enumerate() {
println!("{} -> {}", index, x);
}
Loop Expressions
Rust also supports a general loop
expression. Unlike while and for, a loop expression doesn't have a syntactic place for a condition. It can be used to write infinite loops, which have a place in servers that run indefinitely. Such a loop would have this form:
loop {
// wait for request
// process request
}
It may also be used to write finite loops in which we decide when to exit using a break
statement. Manual exits are useful in repetitions where we wish to test the condition in the middle of an iteration rather than at the beginning or end like we are forced to do with while and do-while loops. For example, this loop prints random comma-separated bytes until it reaches a number greater than 100:
loop {
let number = random::<u8>();
print!("{}", number);
if number > 100 {
break;
}
print!(", ");
}
The break condition is checked between printing the number and printing the separating comma, which means the final number won't be followed by a comma.
The break
statement may be used to return a value to the surrounding context.
You may have been taught to avoid break
statements. Be skeptical of dogma. The intent behind the dogma is a reasonable desire for readable and safe code. If a break
produces readable and safe code, then use it.