[Rust] Understanding Functions: From Basics to Return Values
Functions are the building blocks of any Rust program. You’ve already seen the main function, but today we’re going to dive deeper into how you can create your own! 🛠️
Declaring and Calling Functions
In Rust, we use the fn keyword to define a new function.
Naming Convention: Snake Case
Rust uses snake case for function and variable names. This means all letters are lowercase, and we use underscores (_) to separate words.
Example:
my_cool_function,calculate_distance
The Structure
A function starts with fn, followed by its name and parentheses (). The code inside the curly brackets {} is called the function body.
fn main() {
println!("Hello, world!");
another_function(); // Let's call it!
}
fn another_function() {
println!("Another function.");
}
Cool Fact: In Rust, it doesn't matter if you define another_function before or after the main function. As long as it's defined somewhere the caller can see, the compiler is happy! ✅
Parameters
Parameters are special variables that let you pass data into a function. They are part of the function’s "signature."
fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {value}{unit_label}");
}
Type Annotations are a Must!
When defining parameters, you must declare the type of each one (like i32 or char). This is a deliberate choice in Rust's design. It helps the compiler give you better error messages and means you don't have to label types as often in the rest of your code. 🔍
Statements vs. Expressions
This is a super important distinction in Rust because it's an expression-based language.
| Type | Description | Example |
|---|---|---|
| Statement | Instructions that perform an action but do not return a value. | let y = 6; |
| Expression | Code that evaluates to a resultant value. | 5 + 6, x + 1 |
Why does this matter?
In some languages, you can write x = y = 6. In Rust, you can't, because let y = 6 is a statement and doesn't return anything for x to hold.
However, a block of code inside {} can be an expression:
let y = {
let x = 3;
x + 1 // No semicolon here!
};
// y becomes 4
Notice the missing semicolon on the x + 1 line. In Rust, adding a semicolon turns an expression into a statement, which means it won't return a value! 💡
Functions with Return Values
Functions can send data back to the caller. We declare the return type after an arrow ->.
fn five() -> i32 {
5 // This is an expression that returns 5
}
fn main() {
let x = five();
println!("The value of x is: {x}"); // Prints 5
}
The "Implicit" Return
In Rust, the return value of a function is usually the value of the final expression in its body. You can use the return keyword for an early exit, but most Rustaceans prefer just leaving the semicolon off the last line. 🏃♂️
What happens if you add a semicolon by mistake?
If you write x + 1; in a function that expects to return an i32, you'll get a "mismatched types" error. The semicolon turns it into a statement, returning a "unit type" () instead of the number the compiler was looking for.
Summary
- Use
fnandsnake_casefor functions. 🐍 - Always specify types for parameters. 📋
- Statements do work; Expressions result in values. 💎
- The last expression in a function (no semicolon!) is the return value. 🎁