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
2: Anatomy of a Rust Program
Now that you've got your environment humming, we need to look at how a Rust program is actually put together. It's not just about where the code goes, but how Rust expects the "skeleton" of your application to look. To do this, we're going to build a simple Fuel Efficiency Calculator. It's small, but it forces us to deal with the entry point, helper functions, and imports.
Setting up the front door
Every executable Rust program needs a starting point. In Rust, that's the main function. I like to think of it as the front door of your house; no matter how many rooms (functions) you have inside, the guest always enters here. Let's start by declaring our main function and a simple variable.
fn main() {
let miles = 300.0;
let gallons = 12.0;
println!("Calculating fuel efficiency...");
}
Note that fn is the keyword we use to define a function. I've used floating-point numbers here (300.0) because fuel efficiency is rarely a clean integer. Rust is very strict about types, so keeping those decimals explicit helps the compiler understand we're dealing with floats right from the start.
Breaking logic into a helper function
You could do all the math right inside main, but that's a bad habit. As your programs grow, main should act more like a conductor and less like the whole orchestra. I want a separate function that handles the actual calculation. This is where we see the anatomy of a function signature: the name, the parameters, and the return type.
fn main() {
let miles = 300.0;
let gallons = 12.0;
let mpg = calculate_mpg(miles, gallons);
println!("Your fuel efficiency is {} MPG", mpg);
}
fn calculate_mpg(distance: f64, fuel: f64) -> f64 {
distance / fuel
}
Look at calculate_mpg. The -> f64 tells Rust, "This function will hand back a 64-bit float." If I left that out, Rust would assume the function returns nothing (called a "unit type").
Wait, why is my function returning nothing?
Here is where I usually trip up when I'm writing Rust quickly. I'm going to make a mistake that almost every beginner (and many pros) makes: adding a semicolon where it doesn't belong.
fn calculate_mpg(distance: f64, fuel: f64) -> f64 {
distance / fuel; // I added a semicolon here by habit!
}
If I try to compile this, the compiler will scream at me. Why? Because in Rust, the last expression in a function is the return value if it doesn't have a semicolon. By adding that semicolon, I turned the expression into a statement. A statement doesn't return a value; it just completes. I've effectively told Rust, "Calculate this, then throw the result away and return nothing," which contradicts my -> f64 signature.
The fix is simple: remove the semicolon. It feels weird at first—like you're forgetting something—but it's one of those "Rust-isms" you'll grow to love because it makes the code cleaner.
Pulling in tools from the standard library
Finally, let's make this a bit more robust. What if the user enters 0 for gallons? We'll get an infinity result or a crash. I want to use a tool from the standard library to handle some basic logic. While our example is simple, this is where the use keyword comes in. It's how we bring external modules into scope so we don't have to type the full path every time.
use std::cmp::Ordering;
fn main() {
let miles = 300.0;
let gallons = 12.0;
let mpg = calculate_mpg(miles, gallons);
// Let's compare this to a "gold standard" of 25 MPG
match mpg.partial_cmp(&25.0).unwrap() {
Ordering::Greater => println!("Great efficiency!"),
Ordering::Less => println!("Could be better."),
Ordering::Equal => println!("Exactly average."),
}
}
fn calculate_mpg(distance: f64, fuel: f64) -> f64 {
distance / fuel
}
By adding use std::cmp::Ordering; at the very top, I can use Ordering::Greater instead of typing std::cmp::Ordering::Greater everywhere. This structure—imports at the top, the entry point in the middle, and helper functions at the bottom—is the standard anatomy of a professional Rust file.
📋 Practical Task
Build a Custom Temperature Converter
Your task is to create a Rust program that converts Fahrenheit to Celsius. To do this, you must follow the structural "anatomy" we discussed in the lesson:
- Create a
mainfunction that serves as the entry point. - Create a separate helper function named
fahrenheit_to_celsiusthat takes anf64and returns anf64. - Inside
main, define a temperature variable, call your helper function, and print the result. - Challenge: Ensure your helper function returns the value using an expression (no
returnkeyword and no trailing semicolon on the final line).
Formula: (Fahrenheit - 32) * 5/9
There are no comments for now.