Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
10: Functions
How do I actually set up a function with parameters and a return type?
In Rust, we use the fn keyword. The syntax is pretty strict about types, which I know feels like a chore at first, but it saves you from a mountain of debugging later. You have to explicitly declare the type of every parameter and the type of the value the function returns using an arrow (->).
Let's say we're building a small combat system for a game. We need a way to calculate damage based on a base attack and a critical hit multiplier. It would look like this:
fn calculate_damage(base_attack: u32, multiplier: u32) -> u32 {
base_attack * multiplier
}
Notice that base_attack and multiplier are both u32 (unsigned 32-bit integers). If you try to pass a float or a signed integer in there, the compiler will stop you immediately. I personally prefer being explicit here; it makes the function signature act as documentation for anyone else reading your code.
Why do some people use the return keyword and others just leave the last line without a semicolon?
This is one of those "Rust things" that trips up people coming from C++ or Java. In Rust, almost everything is an expression. An expression evaluates to a value. A statement, on the other hand, just performs an action and returns nothing.
When you put a semicolon at the end of a line, you're telling Rust: "This is a statement, throw the value away." If you omit the semicolon on the very last line of a function, you're creating an expression, and Rust automatically returns that value.
fn add_bonus(score: u32) -> u32 {
let bonus = 10;
score + bonus // No semicolon here! This is the return value.
}
You can still use the return keyword if you want to exit a function early—like inside an if block—but using it on the final line is considered unidiomatic. It's just noisy. Trust the implicit return; it's cleaner.
What happens if I don't want to return anything at all?
Sometimes you just need a function to do something, like logging a message or updating a database, without sending a value back. In these cases, you just leave off the return arrow entirely.
Under the hood, Rust is actually returning a special type called the "unit type," written as (). It's essentially an empty tuple. It means "nothing of interest here."
fn print_combat_log(attacker: &str, damage: u32) {
println!("{} dealt {} damage!", attacker, damage);
// No return type specified, so it returns ()
}
I often use these "void-like" functions to wrap repetitive logic. If you find yourself writing the same three println! calls in different parts of your app, wrap them in a function and keep your main logic uncluttered.
📋 Practical Task
Build an RPG Experience Point Calculator
Your task is to create a small program that manages a player's experience points (XP). You need to implement two functions:
calculate_xp_gain(base_xp: u32, multiplier: f32) -> u32: This function should take a base amount of XP and a float multiplier (e.g., 1.5 for a rested bonus), multiply them, and return the result as au32. Hint: You'll need to cast the result usingas u32.display_player_status(name: &str, total_xp: u32): This function should take the player's name and their total XP, then print a formatted message like: "Player [Name] currently has [XP] experience points."
In your main function, call calculate_xp_gain to determine how much XP was earned from a quest, add it to a total_xp variable, and then call display_player_status to show the result.
There are no comments for now.