C
h
i
L
L
u
.
.
.
Systems Programming Language

The Complete Rust Language Guide

Master Rust — the language that guarantees memory safety without a garbage collector, enabling fearless concurrency and blazing performance.

Why Learn Rust?

Rust was created by Mozilla Research and released in 2010. It has been voted the "most loved programming language" in the Stack Overflow Developer Survey for eight consecutive years. Rust gives you the low-level control of C++ with the safety guarantees of modern languages.

Rust's ownership system enforces memory safety at compile time without needing a garbage collector. This means zero-cost abstractions, no null pointer dereferences, no data races, and no dangling references — all guaranteed before your code even runs.

Rust is used in WebAssembly, game engines, operating systems, embedded systems, and major platforms like Cloudflare, Discord, and the Linux kernel.

Core principle: In Rust, memory safety bugs are compile-time errors, not runtime crashes.

1. Variables & Data Types

Variables and Mutability

In Rust, variables are immutable by default. You must explicitly use 'mut' to allow mutation. This forces intentional design around state changes.

fn main() {
    // Immutable by default
    let x = 5;
    // x = 6; // ERROR: cannot assign twice to immutable variable

    // Mutable variable
    let mut count = 0;
    count += 1;
    println!("Count: {}", count);

    // Shadowing (rebind same name)
    let spaces = "   ";
    let spaces = spaces.len(); // different type is fine

    // Data types
    let integer: i32 = 42;
    let float: f64 = 3.14;
    let boolean: bool = true;
    let character: char = 'R';
    let tuple: (i32, f64, bool) = (500, 6.4, true);
    let array: [i32; 5] = [1, 2, 3, 4, 5];

    println!("{} {} {} {} {}", integer, float, boolean, character, spaces);
    println!("Tuple first: {}", tuple.0);
    println!("Array third: {}", array[2]);
}

Control Flow

Rust's if expressions can return values, making them usable in assignments. The loop, while, and for constructs cover all iteration needs.

fn main() {
    // if as an expression
    let number = 7;
    let description = if number % 2 == 0 { "even" } else { "odd" };
    println!("{} is {}", number, description);

    // loop with return value
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2; // returns 20
        }
    };
    println!("Result: {}", result);

    // for with ranges
    for i in 1..=5 {
        println!("i = {}", i);
    }

    // iterating over a collection
    let animals = ["cat", "dog", "bird"];
    for animal in animals.iter() {
        println!("Animal: {}", animal);
    }
}

2. Ownership & Borrowing

Ownership Rules

Every value has a single owner. When ownership is moved, the original variable is invalid. References allow borrowing without taking ownership.

fn main() {
    // Move semantics
    let s1 = String::from("hello");
    let s2 = s1; // s1 is moved to s2
    // println!("{}", s1); // ERROR: s1 is no longer valid
    println!("{}", s2); // OK

    // Clone to deep copy
    let s3 = s2.clone();
    println!("{} and {}", s2, s3); // both valid

    // Borrowing with references
    let s4 = String::from("world");
    let len = calculate_length(&s4); // borrow, not move
    println!("{} has length {}", s4, len); // s4 still valid

    // Mutable reference
    let mut s5 = String::from("hello");
    change(&mut s5);
    println!("{}", s5);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

fn change(s: &mut String) {
    s.push_str(", world");
}

3. Structs & Enums

Structs and impl blocks

Structs bundle related data. Methods are defined in impl blocks. Enums in Rust are algebraic data types and can hold data in each variant.

#[derive(Debug)]
struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // Associated function (constructor)
    fn new(width: f64, height: f64) -> Rectangle {
        Rectangle { width, height }
    }

    // Method
    fn area(&self) -> f64 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

// Enum with data
enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Triangle(f64, f64, f64),
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle(r) => std::f64::consts::PI * r * r,
            Shape::Rectangle(w, h) => w * h,
            Shape::Triangle(a, b, c) => {
                let s = (a + b + c) / 2.0;
                (s * (s - a) * (s - b) * (s - c)).sqrt()
            }
        }
    }
}

fn main() {
    let rect = Rectangle::new(10.0, 5.0);
    println!("Area: {}", rect.area());
    println!("Square? {}", rect.is_square());
    println!("{:?}", rect);

    let circle = Shape::Circle(3.0);
    println!("Circle area: {:.2}", circle.area());
}

4. Traits & Generics

Defining and Implementing Traits

Traits define shared behavior, similar to interfaces. Generics let you write code that works across multiple types without duplication.

trait Greet {
    fn greeting(&self) -> String;
    fn greet(&self) {
        println!("{}", self.greeting());
    }
}

struct English;
struct Spanish;

impl Greet for English {
    fn greeting(&self) -> String {
        String::from("Hello!")
    }
}

impl Greet for Spanish {
    fn greeting(&self) -> String {
        String::from("Hola!")
    }
}

// Generic function with trait bound
fn print_greeting<T: Greet>(item: &T) {
    item.greet();
}

// Generic struct
struct Pair<T> {
    first: T,
    second: T,
}

impl<T: std::fmt::Display> Pair<T> {
    fn show(&self) {
        println!("({}, {})", self.first, self.second);
    }
}

fn main() {
    print_greeting(&English);
    print_greeting(&Spanish);

    let pair = Pair { first: 10, second: 20 };
    pair.show();
}

5. Error Handling

Result and Option types

Rust has no null values and no exceptions. Instead, it uses Option<T> for potentially absent values and Result<T, E> for operations that can fail. The ? operator propagates errors elegantly.

use std::num::ParseIntError;
use std::fs;

// Option for nullable values
fn find_first_even(numbers: &[i32]) -> Option<i32> {
    numbers.iter().find(|&&x| x % 2 == 0).copied()
}

// Result for fallible operations
fn parse_number(s: &str) -> Result<i32, ParseIntError> {
    s.trim().parse::<i32>()
}

// ? operator for error propagation
fn read_and_parse(filename: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(filename)?;
    let number = content.trim().parse::<i32>()?;
    Ok(number * 2)
}

fn main() {
    // Option
    let numbers = vec![1, 3, 5, 4, 7];
    match find_first_even(&numbers) {
        Some(n) => println!("First even: {}", n),
        None => println!("No even numbers"),
    }

    // Result
    match parse_number("42") {
        Ok(n) => println!("Parsed: {}", n),
        Err(e) => println!("Error: {}", e),
    }

    // if let shorthand
    if let Ok(n) = parse_number("100") {
        println!("Got number: {}", n);
    }
}

6. Fearless Concurrency

Threads and Message Passing

Rust prevents data races at compile time. Threads communicate via channels or share state with Mutex. The ownership system ensures safe concurrent access.

use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;

fn main() {
    // Spawning threads
    let handle = thread::spawn(|| {
        for i in 1..5 {
            println!("Thread: {}", i);
        }
    });
    handle.join().unwrap();

    // Message passing with channels
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let messages = vec!["hi", "from", "the", "thread"];
        for msg in messages {
            tx.send(msg).unwrap();
        }
    });
    for received in rx {
        println!("Got: {}", received);
    }

    // Shared state with Arc<Mutex<T>>
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..5 {
        let c = Arc::clone(&counter);
        let h = thread::spawn(move || {
            let mut num = c.lock().unwrap();
            *num += 1;
        });
        handles.push(h);
    }
    for h in handles { h.join().unwrap(); }
    println!("Counter: {}", *counter.lock().unwrap());
}

Keep Rusting!

Rust's learning curve is steep but the payoff is enormous — memory-safe, high-performance code that you can trust at scale.

Happy Coding with Rust!