Control Flows
Blocks
A block in rust have the following syntax:
{
// statements
}
Theses blocks are expressions, hence they have both a type and a value which is the value of the last expression in the block unless
the block ends with a semicolon, in which case the block evaluates to ()
or there’s a return
statement before the last expression.
If
expressions
If
are also expressions in Rust, so they have a value and a type. The syntax is as follows:
fn main() {
let mut x = 10;
if x % 2 == 0 {
x = x / 2;
} else {
x = 3 * x + 1;
}
}
If let
expressions
If let
expressions are a shorthand for match
expressions that only have one pattern. The syntax is as follows:
fn main() {
let arg = std::env::args().next();
if let Some(value) = arg {
println!("Program name: {value}");
} else {
println!("Missing name?");
}
}
If let
can be can be more concise as it only focuses on one case. A common use is
when working with Option
values. It does not support guards
.
From version 1.65
of Rust, if let
expressions can have an else
branch. This is useful when you want to handle
the case where the pattern does not match.
fn main() {
println!("{:?}", second_word_to_upper("foo bar"));
}
fn second_word_to_upper(s: &str) -> Option<String> {
let mut it = s.split(' ');
let (Some(_), Some(item)) = (it.next(), it.next()) else {
return None;
};
Some(item.to_uppercase())
}
// Some("BAR")
While
loops
While
are very similar to other languages. Nothing special here.
fn main() {
let mut x = 10;
while x > 0 {
println!("{}", x);
x -= 1;
}
}
While let
loops
While let
loops are similar to if let
expressions. They are a shorthand for loop
loops that only have one pattern. The syntax is as follows:
fn main() {
let mut it = std::env::args();
while let Some(value) = it.next() {
println!("Program name: {value}");
}
}
// Program name: /home/sagoez/.cargo/bin/cargo
For
loops
Will automatically call into_iter
on the collection and iterate over the elements:
fn main() {
let mut v = vec![10, 20, 30];
for x in v.iter_mut() {
println!("x: {x}");
*x = *x + 1;
}
println!("{:#?}", v);
for i in (0..10).step_by(2) {
println!("i: {i}");
}
}
Loop
loops
Loop
loops are infinite loops. You can break
, continue
or return
from them.
fn main() {
let mut x = 10;
loop {
x = if x % 2 == 0 {
x / 2
} else {
3 * x + 1
};
if x == 1 {
break;
}
}
println!("Final x: {x}");
}
Match
expressions
Match
expressions are very similar to switch
statements in other languages. The syntax is as follows:
fn main() {
match std::env::args().next().as_deref() {
Some("cat") => println!("Will do cat things"),
Some("ls") => println!("Will ls some files"),
Some("mv") => println!("Let's move some files"),
Some("rm") => println!("Uh, dangerous!"),
None => println!("Hmm, no program name?"),
_ => println!("Unknown program name!"),
}
}
- What happens if
as_deref
is removed?std::env::args().next()
returns anOption<String>
.Option<String>
can’t be matched against.as_deref
convertsOption<String>
toOption<&str>
. Or more generally,Option<T>
toOption<&T::Target>
.&str
can be matched against.
Break
and Continue
labels
- To exit a
loop
early, you can usebreak
. - To skip the rest of the current iteration, you can use
continue
.
break
and continue
can be used with labels to break out of or continue a specific loop.
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}
// Entered the outer loop
// Entered the inner loop
// Exited the outer loop