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_derefis removed?std::env::args().next()returns anOption<String>.Option<String>can’t be matched against.as_derefconvertsOption<String>toOption<&str>. Or more generally,Option<T>toOption<&T::Target>.&strcan be matched against.
Break and Continue labels
- To exit a
loopearly, 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