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.
ownership.rsrust
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 (&).

borrowing.rsrust
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.

Cargo.tomltoml
[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
src/main.rsrust
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.

errors.rsrust
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),
    };
}