🏭 Rust Functions: Your Code Factory
Imagine you’re building a toy factory. Each machine in your factory does one specific job — one paints toys red, another adds wheels, another wraps them in boxes. In Rust, functions are like these machines. You feed them materials (inputs), they do their magic, and out comes something useful!
🎯 What Are Functions?
A function is a reusable block of code that does one job. Instead of writing the same code over and over, you write it once inside a function, give it a name, and call it whenever you need it.
Think of it like this:
- Without functions: Every time you want a sandwich, you write down all 10 steps
- With functions: You just say “make_sandwich()” and it happens!
fn say_hello() {
println!("Hello, friend!");
}
fn main() {
say_hello(); // Prints: Hello, friend!
say_hello(); // Prints: Hello, friend!
}
The Anatomy of a Function
graph TD A["fn keyword"] --> B["function_name"] B --> C["#40; parameters #41;"] C --> D["{ body }"]
Key parts:
fn— Rust’s way of saying “here comes a function!”- name — use
snake_case(lowercase with underscores) - parentheses — hold the inputs (can be empty)
- curly braces — wrap the code that runs
📦 Function Parameters: Feeding the Machine
Parameters are the ingredients you give to your function. They’re like telling your sandwich machine what kind of bread and filling you want.
fn greet(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
greet("Alice"); // Hello, Alice!
greet("Bob"); // Hello, Bob!
}
Multiple Parameters
Your function can take many ingredients!
fn add_numbers(a: i32, b: i32) {
println!("{} + {} = {}", a, b, a + b);
}
fn main() {
add_numbers(5, 3); // 5 + 3 = 8
}
Golden Rule: Every parameter needs a name AND a type. Rust wants to know exactly what it’s getting!
graph LR A["greet#40;name: &str#41;"] --> B["name = label"] A --> C["&str = type"]
🎁 Return Values: What the Machine Gives Back
Some functions don’t just do something — they give you something back. Like a vending machine: you put in money, you get a snack!
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(2, 3);
println!("Result: {}", result); // Result: 5
}
How Return Works
- Use
->followed by the return type - The last expression (without semicolon!) is returned
- Or use
returnkeyword explicitly
fn multiply(x: i32, y: i32) -> i32 {
return x * y; // explicit return
}
fn double(n: i32) -> i32 {
n * 2 // implicit return (no semicolon!)
}
⚠️ Watch that semicolon!
fn broken() -> i32 {
5; // ERROR! Semicolon makes it a statement
}
fn works() -> i32 {
5 // Perfect! This is an expression
}
⚡ Statements vs Expressions: The Secret Sauce
This is Rust’s superpower! Understanding this makes everything click.
Statements: Do Something, Return Nothing
A statement performs an action but doesn’t give back a value. It ends with a semicolon.
let x = 5; // statement
println!("Hi"); // statement
You can’t do this:
let x = (let y = 6); // ERROR! let is a statement
Expressions: Calculate and Give Back
An expression evaluates to a value. Most things in Rust are expressions!
5 // expression → 5
3 + 4 // expression → 7
{
let x = 3;
x + 1 // expression → 4
}
The Magic Block
Curly braces create a block expression:
fn main() {
let y = {
let x = 3;
x + 1 // no semicolon = returns 4
};
println!("y = {}", y); // y = 4
}
graph TD A["{block}"] --> B["let x = 3;"] B --> C["x + 1"] C --> D["Returns 4"]
Simple Rule:
| Type | Ends With | Returns Value? |
|---|---|---|
| Statement | ; |
❌ No |
| Expression | nothing | ✅ Yes |
🚀 Diverging Functions: The Never-Return Club
Some functions never come back. Like a one-way street with no U-turn!
These functions return the special type ! (called “never”).
Common Diverging Functions
fn forever() -> ! {
loop {
// runs forever!
}
}
fn crash() -> ! {
panic!("Something terrible happened!");
}
Why Use Them?
- Infinite loops (game loops, servers)
- Panic situations (unrecoverable errors)
- Exit the program completely
fn main() {
let x: i32 = if true {
5
} else {
panic!("Never reaches here!")
// panic! returns !, which works
// with any expected type!
};
}
The ! type can become any other type, so it fits anywhere!
💬 Comments: Notes to Your Future Self
Comments are like sticky notes in your code. Rust ignores them, but humans love them!
Line Comments
// This is a single-line comment
fn main() {
let x = 5; // comment at end of line
}
Block Comments
/* This is a
multi-line
block comment */
fn calculate() {
/* quick note here */
}
Doc Comments (Super Special!)
/// Adds two numbers together.
///
/// # Examples
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}
Doc comments (///) generate documentation! They’re like instruction manuals for your functions.
graph TD A["// line comment"] --> D["Ignored by compiler"] B["/* block */"] --> D C["/// doc comment"] --> E["Generates docs!"]
🎮 Putting It All Together
Let’s build a tiny calculator using everything we learned!
/// Calculates the area of a rectangle.
fn area(width: i32, height: i32) -> i32 {
width * height // expression - returns!
}
/// Prints a friendly greeting.
fn greet(name: &str) {
// This is a statement
println!("Welcome, {}!", name);
}
fn main() {
greet("Builder");
let w = 5;
let h = 3;
let result = area(w, h);
println!("Area: {}", result); // Area: 15
}
🌟 Quick Summary
| Concept | What It Is | Example |
|---|---|---|
| Function | Reusable code block | fn say_hi() { } |
| Parameters | Inputs with types | fn add(a: i32, b: i32) |
| Return Value | Output using -> |
-> i32 { a + b } |
| Statement | Action, no value | let x = 5; |
| Expression | Evaluates to value | x + 1 |
| Diverging | Never returns (!) |
fn crash() -> ! |
| Comments | Notes for humans | // like this |
🚀 You Did It!
You now understand how Rust functions work! Think of them as your code factory:
- Define the machine (function)
- Feed it inputs (parameters)
- Get outputs back (return values)
- Leave notes (comments)
Functions are the building blocks of every Rust program. Master them, and you can build anything! 🦀✨
