Working Code
Try running the code below in the Rust Playground!
fn greet(name: &str) {
println!("Hello, {name}!");
}
fn main() {
greet("DaleSchool");
greet("Rust");
}
You can create functions with fn. Here we made a function called greet and called it twice from main.
Now let's look at a function that returns a value.
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(3, 5);
println!("3 + 5 = {result}");
}
-> i32 means "this function returns a value of type i32." If you write a value on the last line of a function without a semicolon, that value gets returned.
Try It Yourself
Try adding a semicolon (;) after a + b in the code below. What error do you get?
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(10, 20);
println!("Result: {result}");
}
Adding a semicolon causes an error; removing it makes the code work. Let's find out why below!
"Why?" — Expressions vs. Statements
In Rust, there's a distinction between expressions and statements.
- Expressions produce a value. Things like
3 + 5anda + bare expressions. - Statements don't produce a value. Adding a semicolon (
;) turns something into a statement.
fn double(x: i32) -> i32 {
x * 2 // No semicolon -> expression -> this value gets returned
}
fn main() {
let result = double(7);
println!("Double of 7: {result}");
}
If the last line of a function is an expression, its value is automatically returned. If you add a semicolon, it becomes a statement and returns nothing.
One more thing! Function parameters must always have explicit types. While let bindings can rely on type inference, function parameters cannot omit types.
fn is_even(number: i32) -> bool {
number % 2 == 0
}
fn main() {
println!("Is 4 even? {}", is_even(4));
println!("Is 7 even? {}", is_even(7));
}
Deep Dive
There's also a return keyword
Rust has a return keyword too. You use it when you want to return a value early, in the middle of a function.
fn check_age(age: i32) -> &'static str {
if age < 0 {
return "Age must be 0 or greater";
}
"Valid input"
}
fn main() {
println!("{}", check_age(-1));
println!("{}", check_age(20));
}
For the last line, you can simply omit the semicolon instead of writing return. In Rust, leaving out return is the more idiomatic style.
If a function has a return type, adding a semicolon to the last expression causes an error.
fn add(a: i32, b: i32) -> i32 {
a + b;
}
fn main() {
println!("{}", add(1, 2));
}
Error message:
error[E0308]: mismatched types
--> src/main.rs:1:32
|
1 | fn add(a: i32, b: i32) -> i32 {
| --- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail expression
2 | a + b;
| - help: remove this semicolon to return this value
What this means: "You said the function returns i32, but it's actually returning nothing." Removing the semicolon turns a + b back into an expression, so the value gets returned.
Look at the last line of the error message — help: remove this semicolon to return this value. The compiler is even telling you how to fix it!
- Write a
maxfunction that takes twoi32parameters and returns the larger one. (Hint:if a > b { a } else { b }) - Write a
squarefunction that takes a singlei32parameter and returns its squared value, then call it frommain.
Q1. What does the following function return?
fn mystery(x: i32) -> i32 {
x * 3
}
What happens when you call mystery(4)?
- A) 3
- B) 4
- C) 12
- D) Compilation error
Q2. Can you omit the type of a function parameter in Rust?
- A) You can always omit it
- B) You can never omit it
- C) You can omit it if the compiler can infer the type
- D) You can omit it if you add
mut
Q3. What is the output of this code?
fn half(x: i32) -> i32 {
x / 2
}
fn main() {
println!("{}", half(7));
}
- A) 3.5
- B) 3
- C) 4
- D) Compilation error