For decades, backend engineers had to choose between extreme performance (C/C++) and memory safety (Java/Node.js). C/C++ requires manual memory management, leading to segmentation faults and severe security vulnerabilities (buffer overflows). Java and Node.js use a Garbage Collector to ensure safety, but this introduces unpredictable latency spikes. Rust eliminates this compromise. It delivers the bare-metal speed of C, while guaranteeing memory safety at compile time.
Module 1: The Ownership Model
Rust achieves memory safety without a garbage collector through its revolutionary Ownership system. The compiler enforces three strict rules:
The Rules of Ownership
- 1. Each value in Rust has a variable that’s called its 'owner'.
- 2. There can only be ONE owner at a time.
- 3. When the owner goes out of scope, the value will be instantly dropped from memory.
fn main() {
// s1 owns the String data on the Heap
let s1 = String::from("hello");
// The ownership is MOVED to s2. s1 is now mathematically invalid.
let s2 = s1;
// println!("{}", s1); // COMPILER ERROR! s1 no longer exists.
println!("{}", s2); // This works.
} // s2 goes out of scope here. The memory is immediately freed.Module 2: Borrowing and Lifetimes
If you want to pass a variable to a function without surrendering ownership, you 'Borrow' it using a reference (&).
fn calculate_length(s: &String) -> usize {
// We borrowed 's'. We can read it, but we cannot modify it.
s.len()
}
fn main() {
let my_string = String::from("Rust is fast");
// Pass a reference (a pointer) to the function
let length = calculate_length(&my_string);
// my_string is still valid here because we only borrowed it!
println!("The length of '{}' is {}.", my_string, length);
}Module 3: Building a Web Server with Axum
Axum is the industry-standard web framework for Rust, built on top of the ultra-fast Tokio asynchronous runtime. It uses declarative routing and macros for extreme efficiency.
[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"use axum::{
routing::{get, post},
http::StatusCode,
Json,
Router,
};
use serde::{Deserialize, Serialize};
// We use Serde to automatically convert JSON into Rust Structs
#[derive(Serialize, Deserialize)]
struct CreateUser {
username: String,
}
#[derive(Serialize)]
struct UserResponse {
id: u64,
username: String,
}
// Asynchronous handler function
async fn create_user(Json(payload): Json<CreateUser>) -> (StatusCode, Json<UserResponse>) {
// In a real app, save to a database here
let user = UserResponse {
id: 1337,
username: payload.username,
};
// Return HTTP 201 Created and the JSON response
(StatusCode::CREATED, Json(user))
}
#[tokio::main]
async fn main() {
// Build the application routing tree
let app = Router::new()
.route("/", get(|| async { "Hello, Rust!" }))
.route("/users", post(create_user));
// Bind to the port and run the server
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("Server running on port 3000");
axum::serve(listener, app).await.unwrap();
}Module 4: Error Handling (Result Enum)
Rust has no Exceptions. No try/catch. Errors are handled via the Result enum, forcing you to acknowledge failure states before your code can compile.
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
// The compiler forces us to handle both the Ok and Err variants
let file = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}