Functions and Control Flow
Functions
Define functions with fn. Arguments and return types need type annotations:
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(1, 2);
println!("{}", result); // 3
}
Did you notice — there's no return in add. In Rust, the last expression becomes the return value. Be careful: adding a semicolon ; turns it into a "statement" and it stops returning a value:
fn add(a: i32, b: i32) -> i32 {
a + b // ← no semicolon = returns this value
}
fn add_wrong(a: i32, b: i32) -> i32 {
a + b; // ← semicolon = doesn't return a value = compile error
}
You can also use return to return explicitly:
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
Use return when you want to exit early. When the last expression can do the job, writing it without a semicolon is the Rust way.
Functions That Return Nothing
Functions that don't return anything omit the ->:
fn greet(name: String) {
println!("Hello, {}!", name);
}
if / else
fn main() {
let x = 42;
if x > 0 {
println!("positive");
} else if x < 0 {
println!("negative");
} else {
println!("zero");
}
}
No () needed around the condition. The compiler will tell you they're unnecessary if you add them.
if Is an Expression
Rust's if is an expression, so it can return a value:
fn main() {
let x = 42;
let label = if x > 0 { "positive" } else { "negative" };
println!("{}", label);
}
Use it instead of the ternary operator (? :).
match
match is pattern matching. A powered-up version of switch from other languages:
fn main() {
let x = 2;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("other"), // _ matches everything else
}
}
_ is the default case (switch's default).
match is also an expression, so it can return a value:
fn main() {
let x = 2;
let name = match x {
1 => "one",
2 => "two",
_ => "other",
};
println!("{}", name); // two
}
match really shines when combined with enums in later chapters.
for Loop
fn main() {
// 0 to 4
for i in 0..5 {
println!("{}", i);
}
// Looping over an array (Vec)
let names = vec!["Alice", "Bob", "Charlie"];
for name in names {
println!("Hello, {}!", name);
}
}
0..5 is a range meaning "0 or more, less than 5." 0..=5 means "0 or more, 5 or less."
vec![...]is a macro that creates a Vec (dynamic array). More on this in a later chapter.
while Loop
fn main() {
let mut count = 0;
while count < 5 {
println!("{}", count);
count += 1;
}
}
loop (Infinite Loop)
fn main() {
let mut count = 0;
loop {
if count >= 5 {
break;
}
println!("{}", count);
count += 1;
}
}
Use break to exit. loop can also be used as an expression:
fn main() {
let mut count = 0;
let final_count = loop {
count += 1;
if count >= 10 {
break count; // passing a value to break makes it the loop's return value
}
};
println!("{}", final_count); // 10
}
Functions and control flow are done. Next, let's learn structs and enums to create our own data types.