Rust Handbook

Rust is a systems programming language designed by Mozilla and first released in 2015. Its ownership and borrow-checker model guarantees memory and thread safety at compile time — without a garbage collector. Zero-cost abstractions, LLVM-backed codegen, and no runtime overhead give it C/C++-level throughput. Rust has been voted "most loved language" in Stack Overflow surveys every year since 2016 and is now used in the Linux kernel, Windows kernel, Android, Cloudflare Workers, Amazon S3, and the Rust-rewritten parts of Firefox.

Pick Rust when

  • Memory safety without a GC — the borrow checker eliminates use-after-free, double-free, null pointer dereference, and data races at compile time. This is Rust's defining property.
  • Systems programming with safety — writing an OS component, device driver, embedded firmware, or database storage engine where C would be the alternative. Rust is now the only language besides C/C++ allowed in the Linux kernel.
  • WebAssembly — Rust compiles to WASM with the smallest possible runtime footprint. It is the dominant language for WASM-based edge compute (Cloudflare Workers, Fastly Compute).
  • CLI tools — Rust produces statically linked binaries with no runtime dependency. Tools like ripgrep, fd, bat, and exa replaced their Unix equivalents with Rust implementations that are measurably faster.
  • High-performance network services — async Rust (Tokio, async-std) handles millions of concurrent connections with predictable latency. No GC pauses, no JVM warm-up.
  • Replacing C/C++ in an existing system — Rust has an excellent FFI for calling C and being called by C. You can rewrite one module at a time.

Think twice before choosing Rust when

  • You need to move fast — the borrow checker has a steep learning curve. Fighting the compiler for the first few weeks is normal. Productivity is lower than Go, Python, or TypeScript until the team is proficient.
  • Prototyping or exploratory code — Rust's strictness makes throw-away code expensive to write. Use Python or TypeScript to validate ideas, then rewrite the hot path in Rust if needed.
  • You need a large existing ecosystem — the crate ecosystem (crates.io) is growing fast but is younger than npm or PyPI. Niche domain libraries may not exist yet.
  • Compile times are critical — Rust compilation is slow for large projects. Incremental builds help, but cold builds of a large Rust project can take minutes.

Rust vs. its closest alternatives

  • Rust vs C — same performance, but Rust prevents entire classes of C bugs at compile time. For new code, Rust is almost always safer. For targeting every obscure architecture or integrating into a C-only toolchain, C wins.
  • Rust vs C++ — Rust's safety guarantees are stricter. C++ is more expressive and has a larger ecosystem, but memory bugs are common. New systems projects with no C++ legacy should strongly consider Rust.
  • Rust vs Go — Go is much easier to learn and has a built-in scheduler. Rust is faster (no GC, predictable latency), and safer. Go for developer productivity and ops simplicity; Rust for systems programming and maximum performance.

Resources

Topics

Variables & Types

rust
// Immutable by default — must opt-in to mutability
let x = 5;           // i32 inferred
let mut y = 10;      // mutable
y += 1;

// Explicit types
let a: i32  = -100;
let b: u64  = 1_000_000;  // underscores for readability
let c: f64  = 3.14;
let d: bool = true;
let e: char = 'Z';         // Unicode scalar value
let s: &str = "hello";     // string slice (borrowed)
let owned: String = String::from("world");

// Constants — must be type-annotated, computed at compile time
const MAX_POINTS: u32 = 100_000;
static APP_NAME: &str = "myapp";   // static lifetime

// Shadowing — rebind with same name (may change type)
let z = 5;
let z = z + 1;         // new binding, shadows previous
let z = z.to_string(); // now &str -> shadow changes type

// Numeric types cheat-sheet
// Signed:   i8 i16 i32 i64 i128 isize
// Unsigned: u8 u16 u32 u64 u128 usize
// Float:    f32 f64
// usize / isize  = pointer-width (used for indexing)

// Casting
let n: i32 = 256;
let m = n as u8;   // truncates to 0 (wrapping cast)

// Tuples
let tup: (i32, f64, bool) = (500, 6.4, true);
let (tx, ty, tz) = tup;   // destructure
let first = tup.0;         // index access

// Arrays — fixed length, stack-allocated
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 10];       // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let len = arr.len();       // 5

Ownership

rust
// RULE 1: Each value has exactly one owner.
// RULE 2: When the owner goes out of scope, the value is dropped.
// RULE 3: There can only be one owner at a time.

fn main() {
    // Move semantics — heap-allocated String
    let s1 = String::from("hello");
    let s2 = s1;          // s1 is MOVED into s2; s1 is no longer valid
    // println!("{}", s1); // compile error: value borrowed after move

    // Clone — explicit deep copy
    let s3 = String::from("hello");
    let s4 = s3.clone();  // both s3 and s4 are valid
    println!("s3={} s4={}", s3, s4);

    // Copy types — stack-only types implement Copy (i32, bool, f64, char, tuples of Copy)
    let n1: i32 = 5;
    let n2 = n1;   // n1 is COPIED, not moved
    println!("n1={} n2={}", n1, n2);   // both valid

    // Ownership through functions
    let s = String::from("world");
    takes_ownership(s);      // s is moved into function
    // s is invalid here

    let x = 5;
    makes_copy(x);           // x is copied; still valid
    println!("x={}", x);

    // Return ownership
    let s5 = gives_ownership();   // function returns ownership to s5
    let s6 = String::from("hello");
    let s7 = takes_and_gives_back(s6);   // s6 moved in, s7 gets ownership back
}

fn takes_ownership(s: String) { println!("{}", s); }  // s dropped here
fn makes_copy(n: i32)         { println!("{}", n); }
fn gives_ownership() -> String { String::from("new") }
fn takes_and_gives_back(s: String) -> String { s }

Borrowing & References

rust
// Borrowing = taking a reference without taking ownership
fn main() {
    let s = String::from("hello");

    // Shared (immutable) reference — & — many allowed simultaneously
    let r1 = &s;
    let r2 = &s;
    println!("r1={} r2={}", r1, r2);   // both fine

    // Mutable reference — &mut — ONLY ONE at a time
    let mut s2 = String::from("hello");
    {
        let r3 = &mut s2;
        r3.push_str(", world");
    }  // r3 goes out of scope here
    println!("{}", s2);   // now we can use s2 again

    // Cannot mix shared + mutable borrow of same value simultaneously
    // let r4 = &s2;
    // let r5 = &mut s2;   // compile error

    // References must not outlive the data (dangling reference prevention)
    let reference = dangle_safe();
    println!("{}", reference);

    // Slices — references to a contiguous sequence
    let arr = [1, 2, 3, 4, 5];
    let slice: &[i32] = &arr[1..3];   // [2, 3]
    let s3 = String::from("hello world");
    let word: &str = &s3[0..5];       // "hello"
    println!("slice={:?} word={}", slice, word);
}

// Return owned String instead of reference to local
fn dangle_safe() -> String {
    String::from("owned")
}

// Lifetime intro: compiler ensures this reference is valid
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' { return &s[0..i]; }
    }
    &s[..]
}

Control Flow

rust
fn main() {
    // if / else if / else — expressions, no parens needed
    let n = 7;
    let msg = if n < 0 { "negative" } else if n == 0 { "zero" } else { "positive" };
    println!("{}", msg);

    // match — exhaustive, expression
    let x: i32 = 2;
    match x {
        1       => println!("one"),
        2 | 3   => println!("two or three"),
        4..=10  => println!("four to ten"),
        _       => println!("other"),   // catch-all
    }

    // match returns a value
    let description = match x {
        1 => "one",
        _ => "many",
    };

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

    // while
    let mut n = 3;
    while n != 0 { n -= 1; }

    // for over a range
    for i in 0..5 { print!("{}  ", i); }   // 0 1 2 3 4
    for i in 0..=5 { print!("{}  ", i); }  // 0 1 2 3 4 5

    // for over a collection
    let v = vec![10, 20, 30];
    for val in &v { println!("{}", val); }
    for (i, val) in v.iter().enumerate() { println!("{}:{}", i, val); }

    // loop labels for nested breaks
    'outer: for i in 0..5 {
        for j in 0..5 {
            if i == 2 && j == 2 { break 'outer; }
        }
    }

    // while let
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() { println!("{}", top); }
}

Functions

rust
// Functions declared with fn, snake_case by convention
fn add(a: i32, b: i32) -> i32 {
    a + b   // last expression is returned (no semicolon)
}

// Explicit return
fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 { return None; }
    Some(a / b)
}

// Multiple return via tuple
fn min_max(v: &[i32]) -> (i32, i32) {
    let min = *v.iter().min().unwrap();
    let max = *v.iter().max().unwrap();
    (min, max)
}

// Unit return — () — implicit when no -> type
fn greet(name: &str) { println!("Hello, {}!", name); }

// Closures — anonymous functions capturing environment
fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(f(x)) }

fn main() {
    println!("{}", add(3, 4));      // 7
    println!("{:?}", min_max(&[3, 1, 4, 1, 5, 9]));  // (1, 9)

    // Closure with type inference
    let square = |x| x * x;
    let double = |x: i32| -> i32 { x * 2 };
    println!("{}", apply_twice(double, 3));   // 12

    // Move closure — takes ownership of captured variables
    let s = String::from("hello");
    let print_s = move || println!("{}", s);
    print_s();
    // s is no longer available here

    // Higher-order: functions as parameters and return values
    let ops: Vec<Box<dyn Fn(i32) -> i32>> = vec![
        Box::new(|x| x + 1),
        Box::new(|x| x * 2),
    ];
    let result: i32 = ops.iter().fold(5, |acc, f| f(acc));
    println!("{}", result);   // (5+1)*2 = 12
}

Structs & impl

rust
use std::fmt;

// Struct definition
struct Point { x: f64, y: f64 }

// Tuple struct
struct Color(u8, u8, u8);

// Unit struct (for trait implementations)
struct Unit;

// Struct with lifetime
struct Important<"a> { part: &"a str }

// impl block — associated functions and methods
impl Point {
    // Associated function (no self) — called as Point::new(...)
    fn new(x: f64, y: f64) -> Self { Point { x, y } }
    fn origin() -> Self { Point { x: 0.0, y: 0.0 } }

    // Methods take &self (read), &mut self (mutate), or self (consume)
    fn distance(&self, other: &Point) -> f64 {
        ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
    }
    fn translate(&mut self, dx: f64, dy: f64) {
        self.x += dx;
        self.y += dy;
    }
}

// Implement Display trait
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

// Struct update syntax
#[derive(Debug, Clone)]
struct Config {
    debug: bool,
    verbose: bool,
    timeout: u32,
}

fn main() {
    let p1 = Point::new(0.0, 0.0);
    let p2 = Point { x: 3.0, y: 4.0 };
    println!("distance: {}", p1.distance(&p2));   // 5.0
    println!("p2: {}", p2);                        // (3, 4)

    let Color(r, g, b) = Color(255, 128, 0);
    println!("rgb({}, {}, {})", r, g, b);

    let base = Config { debug: false, verbose: false, timeout: 30 };
    let debug_cfg = Config { debug: true, ..base };  // struct update
    println!("{:?}", debug_cfg);
}

Enums, Option & Result

rust
// Enum variants can carry data — like algebraic data types
#[derive(Debug)]
enum Shape {
    Circle(f64),               // tuple variant
    Rectangle { w: f64, h: f64 },  // struct variant
    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()
            }
        }
    }
}

// Option<T> — the absence of null
fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 { None } else { Some(a / b) }
}

// Result<T, E> — recoverable errors
fn parse_int(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.trim().parse::<i32>()
}

fn main() {
    let c = Shape::Circle(5.0);
    println!("area: {:.2}", c.area());

    // Option usage
    match divide(10.0, 2.0) {
        Some(v) => println!("result: {}", v),
        None    => println!("division by zero"),
    }

    // Option combinator methods
    let opt: Option<i32> = Some(42);
    let doubled = opt.map(|x| x * 2);               // Some(84)
    let filtered = opt.filter(|&x| x > 50);          // None
    let or_else = filtered.unwrap_or(0);              // 0
    let unwrap_or_default: i32 = None.unwrap_or_default();  // 0

    // if let — concise single-variant match
    if let Some(v) = divide(9.0, 3.0) {
        println!("{}", v);  // 3
    }

    // Result usage
    match parse_int("42") {
        Ok(n)  => println!("parsed: {}", n),
        Err(e) => println!("error: {}", e),
    }
    let n: i32 = parse_int("  7  ").unwrap_or(0);
    let n2 = parse_int("bad").unwrap_or_else(|e| { eprintln!("warn: {}", e); -1 });
}

Traits

rust
use std::fmt;

// Trait definition — interface contract
trait Summary {
    fn summarize(&self) -> String;

    // Default implementation (can be overridden)
    fn preview(&self) -> String {
        format!("{}...", &self.summarize()[..20.min(self.summarize().len())])
    }
}

struct Article { title: String, body: String }
struct Tweet   { username: String, content: String }

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}", self.title, self.body)
    }
}
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

// Trait bound in function signature
fn notify(item: &impl Summary) { println!("Breaking: {}", item.summarize()); }

// Equivalent with where clause (cleaner for complex bounds)
fn notify_generic<T>(item: &T) where T: Summary + fmt::Debug {
    println!("{:?} says: {}", item, item.summarize());
}

// Returning impl Trait (concrete type hidden from caller)
fn make_summarizable() -> impl Summary {
    Tweet { username: String::from("bot"), content: String::from("hello") }
}

// Common derivable traits
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct Score(u32);

// Operator overloading via std::ops
use std::ops::Add;
#[derive(Debug, Clone, Copy)]
struct Vec2 { x: f64, y: f64 }

impl Add for Vec2 {
    type Output = Vec2;
    fn add(self, rhs: Vec2) -> Vec2 { Vec2 { x: self.x + rhs.x, y: self.y + rhs.y } }
}

// Display
impl fmt::Display for Vec2 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn main() {
    let a = Article { title: String::from("Rust"), body: String::from("is awesome") };
    notify(&a);
    let v1 = Vec2 { x: 1.0, y: 2.0 };
    let v2 = Vec2 { x: 3.0, y: 4.0 };
    println!("{}", v1 + v2);   // (4, 6)
}

Generics

rust
// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest { largest = item; }
    }
    largest
}

// Generic struct
#[derive(Debug)]
struct Pair<T> { first: T, second: T }

impl<T> Pair<T> {
    fn new(first: T, second: T) -> Self { Pair { first, second } }
}

// Conditional method impl — only when T: Display + PartialOrd
use std::fmt;
impl<T: fmt::Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.first >= self.second {
            println!("Largest is first: {}", self.first);
        } else {
            println!("Largest is second: {}", self.second);
        }
    }
}

// Multiple generic parameters
fn zip_map<A, B, C, F>(a: &[A], b: &[B], f: F) -> Vec<C>
where F: Fn(&A, &B) -> C {
    a.iter().zip(b.iter()).map(|(x, y)| f(x, y)).collect()
}

// Generic enum (like Option/Result in std)
#[derive(Debug)]
enum Either<L, R> { Left(L), Right(R) }

// Turbofish syntax for type parameter specification
fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    println!("largest: {}", largest(&numbers));   // 100

    let p = Pair::new(5, 10);
    p.cmp_display();

    let sums = zip_map(&[1, 2, 3], &[10, 20, 30], |a, b| a + b);
    println!("{:?}", sums);   // [11, 22, 33]

    // Turbofish
    let parsed = "42".parse::<i32>().unwrap();
    let collected = (0..5).collect::<Vec<_>>();
    println!("parsed={} collected={:?}", parsed, collected);
}

Collections

rust
use std::collections::{HashMap, HashSet};

fn main() {
    // Vec<T> — growable array
    let mut v: Vec<i32> = Vec::new();
    v.push(1); v.push(2); v.push(3);
    let v2 = vec![4, 5, 6];          // macro shorthand

    println!("len={} cap={}", v.len(), v.capacity());
    println!("first={:?}", v.get(0));   // Some(1) — safe access
    println!("raw={}", v[0]);            // 1 — panics if OOB

    v.extend(&v2);
    v.retain(|&x| x % 2 == 0);          // keep evens
    let doubled: Vec<i32> = v.iter().map(|&x| x * 2).collect();
    println!("{:?}", doubled);

    // String — owned, heap-allocated, UTF-8
    let mut s = String::new();
    s.push_str("hello");
    s.push(' ');
    s += "world";                // AddAssign calls push_str
    let joined = format!("{} {}", "foo", "bar");
    println!("bytes={} chars={}", s.len(), s.chars().count());

    // String slicing (byte indices — must be char boundaries)
    let hello = &s[0..5];
    // Iterate chars safely
    for c in s.chars() { print!("{}", c); }

    // HashMap<K, V>
    let mut scores: HashMap<String, i32> = HashMap::new();
    scores.insert(String::from("Alice"), 10);
    scores.insert(String::from("Bob"), 20);

    // entry API — insert if absent
    scores.entry(String::from("Alice")).or_insert(50);   // no-op, already exists
    scores.entry(String::from("Carol")).or_insert(30);   // inserts Carol=30

    // Increment with entry
    let text = "hello world hello";
    let mut word_count: HashMap<&str, i32> = HashMap::new();
    for word in text.split_whitespace() {
        *word_count.entry(word).or_insert(0) += 1;
    }
    println!("{:?}", word_count);

    // HashSet<T>
    let mut set: HashSet<i32> = HashSet::new();
    set.insert(1); set.insert(2); set.insert(3);
    let set2: HashSet<i32> = [2, 3, 4].iter().cloned().collect();

    let union: HashSet<_>        = set.union(&set2).collect();
    let intersect: HashSet<_>    = set.intersection(&set2).collect();
    let difference: HashSet<_>   = set.difference(&set2).collect();
    println!("union={:?} intersect={:?} diff={:?}", union, intersect, difference);
}

Error Handling

rust
use std::fmt;
use std::num::ParseIntError;

// Custom error type
#[derive(Debug)]
enum AppError {
    ParseError(ParseIntError),
    NegativeNumber(i32),
    TooBig(i32),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::ParseError(e)    => write!(f, "parse error: {}", e),
            AppError::NegativeNumber(n) => write!(f, "negative number: {}", n),
            AppError::TooBig(n)         => write!(f, "number too big: {}", n),
        }
    }
}

// Implement std::error::Error for interoperability
impl std::error::Error for AppError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self { AppError::ParseError(e) => Some(e), _ => None }
    }
}

// From conversions enable ? operator
impl From<ParseIntError> for AppError {
    fn from(e: ParseIntError) -> Self { AppError::ParseError(e) }
}

fn parse_bounded(s: &str) -> Result<i32, AppError> {
    let n: i32 = s.trim().parse()?;   // ? converts ParseIntError via From
    if n < 0  { return Err(AppError::NegativeNumber(n)); }
    if n > 100 { return Err(AppError::TooBig(n)); }
    Ok(n)
}

// ? in main requires Box<dyn Error>
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Result combinators
    let doubled = parse_bounded("21").map(|n| n * 2)?;
    println!("doubled: {}", doubled);   // 42

    let result = parse_bounded("bad")
        .map_err(|e| format!("failed: {}", e))
        .unwrap_or_else(|msg| { eprintln!("{}", msg); 0 });

    // and_then chains fallible operations
    let chained = parse_bounded("50")
        .and_then(|n| if n == 50 { Ok(n * 3) } else { Err(AppError::TooBig(n)) });
    println!("{:?}", chained);   // Ok(150)

    // panic! — for unrecoverable bugs (not expected errors)
    // let v: Vec<i32> = vec![];
    // let _ = v[0];   // panics with index out of bounds

    // unwrap/expect — use only in tests or when truly impossible to fail
    let n: i32 = "42".parse().expect("hardcoded literal; always valid");
    println!("n={}", n);

    Ok(())
}

Iterators

rust
fn main() {
    let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // Lazy iterator chain — nothing executes until consumed
    let result: Vec<i32> = v.iter()
        .filter(|&&x| x % 2 == 0)   // keep evens: [2,4,6,8,10]
        .map(|&x| x * x)             // square:     [4,16,36,64,100]
        .take(3)                     // first 3:    [4,16,36]
        .collect();
    println!("{:?}", result);

    // fold — general reduction
    let sum: i32 = v.iter().fold(0, |acc, &x| acc + x);
    println!("sum={}", sum);   // 55

    // Common consumers
    let any_even  = v.iter().any(|&x| x % 2 == 0);   // true
    let all_pos   = v.iter().all(|&x| x > 0);          // true
    let max       = v.iter().max();                     // Some(10)
    let count5    = v.iter().filter(|&&x| x > 5).count(); // 5
    let position  = v.iter().position(|&x| x == 7);    // Some(6)

    // flat_map — map then flatten
    let words = vec!["hello world", "foo bar"];
    let chars: Vec<&str> = words.iter()
        .flat_map(|s| s.split_whitespace())
        .collect();
    println!("{:?}", chars);   // ["hello", "world", "foo", "bar"]

    // zip + unzip
    let keys = vec!['a', 'b', 'c'];
    let vals = vec![1, 2, 3];
    let zipped: Vec<_> = keys.iter().zip(vals.iter()).collect();
    println!("{:?}", zipped);

    // chain
    let a = vec![1, 2, 3];
    let b = vec![4, 5, 6];
    let chained: Vec<_> = a.iter().chain(b.iter()).collect();

    // Custom Iterator
    struct Counter { count: u32, max: u32 }
    impl Counter {
        fn new(max: u32) -> Counter { Counter { count: 0, max } }
    }
    impl Iterator for Counter {
        type Item = u32;
        fn next(&mut self) -> Option<u32> {
            if self.count < self.max { self.count += 1; Some(self.count) }
            else { None }
        }
    }
    let pairs: Vec<_> = Counter::new(5).zip(Counter::new(5).skip(1)).collect();
    println!("{:?}", pairs);   // [(1,2),(2,3),(3,4),(4,5)]
}

Closures

rust
fn main() {
    // Closures capture their environment
    let x = 10;
    let add_x = |n| n + x;       // borrows x (Fn)
    println!("{}", add_x(5));     // 15; x still usable

    let mut count = 0;
    let mut increment = || { count += 1; count };  // borrows mutably (FnMut)
    println!("{}", increment());  // 1
    println!("{}", increment());  // 2
    // println!("{}", count);   // error: borrowed mutably above

    // move — takes ownership of captured vars (needed for threads)
    let s = String::from("hello");
    let owns_s = move || println!("{}", s);
    owns_s();
    // s no longer accessible here

    // Fn, FnMut, FnOnce — closure trait hierarchy
    // FnOnce  — can be called once (consumes captures); all closures implement this
    // FnMut   — can be called multiple times with mutation; FnOnce + FnMut
    // Fn      — can be called any times; Fn implies FnMut implies FnOnce
    fn call_once<F: FnOnce() -> String>(f: F) -> String { f() }
    fn call_many<F: Fn() -> i32>(f: F) -> i32 { f() + f() }

    let s2 = String::from("world");
    let greeting = call_once(move || format!("hello {}", s2));
    println!("{}", greeting);

    let value = 7;
    let total = call_many(|| value * 2);
    println!("{}", total);   // 28

    // Returning closures — must use Box<dyn Fn>
    fn make_adder(n: i32) -> Box<dyn Fn(i32) -> i32> {
        Box::new(move |x| x + n)
    }
    let add5 = make_adder(5);
    println!("{}", add5(10));   // 15

    // Closure as function pointer (fn) — when no capture
    let f: fn(i32) -> i32 = |x| x * 2;   // fn pointer, not closure
    println!("{}", f(4));   // 8
}

Lifetimes

rust
// Lifetime annotations tell the compiler how long references must stay valid.
// The compiler INFERS most lifetimes; annotations required when ambiguous.

// "a is a lifetime parameter: both inputs and output share lifetime "a
fn longest<"a>(x: &"a str, y: &"a str) -> &"a str {
    if x.len() > y.len() { x } else { y }
}

// Struct holding a reference — must annotate lifetime
struct Excerpt<'a> {
    part: &'a str,
}

impl<"a> Excerpt<"a> {
    fn level(&self) -> i32 { 3 }

    // Lifetime elision rules apply — 'b inferred from &self
    fn announce(&self, announcement: &str) -> &str {
        println!("Attention: {}", announcement);
        self.part
    }
}

// 'static lifetime — reference valid for entire program duration
// String literals are 'static
fn static_str() -> &"static str { "I live forever' }

// Multiple lifetime parameters
fn first_or_second<"a, "b>(first: &"a str, _second: &"b str) -> &'a str {
    first
}

// Lifetime bounds on generic types: T must outlive 'a
struct Wrapper<"a, T: "a> {
    value: &'a T,
}

fn main() {
    let string1 = String::from("long string");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
        println!("longest: {}", result);
    }
    // result cannot be used here — string2 dropped

    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a sentence");
    let excerpt = Excerpt { part: first_sentence };
    println!("excerpt: {}", excerpt.part);
}

// Lifetime elision rules (compiler applies automatically):
// 1. Each reference parameter gets its own lifetime parameter
// 2. If exactly one input lifetime, assign it to all output lifetimes
// 3. If one of the inputs is &self or &mut self, assign its lifetime to all outputs

Modules & Visibility

rust
// src/main.rs or src/lib.rs

// Inline module
mod math {
    // Private by default — pub to expose
    pub fn add(a: i32, b: i32) -> i32 { a + b }
    pub fn sub(a: i32, b: i32) -> i32 { a - b }

    // Nested module
    pub mod trig {
        pub fn sin(x: f64) -> f64 { x.sin() }
    }

    // pub(crate) — visible within crate only
    pub(crate) fn internal() {}

    // pub(super) — visible to parent module
    pub(super) fn parent_visible() {}
}

// Bring into scope with use
use math::add;
use math::trig::sin;

// Glob import (use sparingly)
// use math::*;

// Rename with as
use math::sub as subtract;

// Re-export with pub use
pub use math::add as public_add;

fn main() {
    println!("{}", add(3, 4));
    println!("{}", subtract(10, 4));
    println!("{:.4}", sin(1.0));

    // Absolute path
    let r = math::add(1, 2);

    // Nested use paths
    // use std::{cmp::Ordering, io};
    // use std::io::{self, Write};
}

// --- File-based modules ---
// src/lib.rs:
//   pub mod garden;      <- loads src/garden.rs or src/garden/mod.rs
//
// src/garden.rs:
//   pub mod vegetables;  <- loads src/garden/vegetables.rs
//
// src/garden/vegetables.rs:
//   pub struct Asparagus {}
//
// Usage:
//   use crate::garden::vegetables::Asparagus;
//   use super::sibling_module::Thing;

Concurrency

rust
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;   // multi-producer, single-consumer channels

fn main() {
    // Spawn a thread — closure must be 'static (move ownership in)
    let handle = thread::spawn(|| {
        for i in 0..10 {
            println!("spawned: {}", i);
            thread::sleep(std::time::Duration::from_millis(1));
        }
    });

    for i in 0..5 { println!("main: {}", i); }
    handle.join().unwrap();   // wait for thread to finish

    // Move data into thread
    let v = vec![1, 2, 3];
    let h = thread::spawn(move || println!("vector: {:?}", v));
    h.join().unwrap();

    // Channels — message passing
    let (tx, rx) = mpsc::channel();

    // Clone tx for multiple producers
    let tx2 = tx.clone();
    thread::spawn(move || { tx.send(String::from("hello")).unwrap(); });
    thread::spawn(move || { tx2.send(String::from("world")).unwrap(); });

    // rx is an iterator (blocks until message arrives, stops when all senders dropped)
    for received in rx { println!("got: {}", received); }

    // Shared state: Arc<Mutex<T>>
    // Arc = atomically reference-counted (thread-safe Rc)
    // Mutex = mutual exclusion
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let h = thread::spawn(move || {
            let mut num = counter.lock().unwrap();   // blocks until lock acquired
            *num += 1;
        });
        handles.push(h);
    }
    for h in handles { h.join().unwrap(); }
    println!("counter: {}", *counter.lock().unwrap());   // 10

    // RwLock — multiple readers or one writer
    use std::sync::RwLock;
    let lock = Arc::new(RwLock::new(5));
    let r1 = lock.read().unwrap();   // shared read
    let r2 = lock.read().unwrap();
    println!("r1={} r2={}", *r1, *r2);
    drop(r1); drop(r2);
    *lock.write().unwrap() = 10;   // exclusive write
}

Async / Await

rust
// Async requires a runtime — tokio is the most common
// Cargo.toml: tokio = { version = "1", features = ["full"] }

use tokio::time::{sleep, Duration};
use tokio::sync::mpsc;

// async fn returns impl Future<Output = T>
async fn fetch_data(id: u32) -> String {
    sleep(Duration::from_millis(10)).await;   // .await yields to executor
    format!("data-{}", id)
}

// async blocks
async fn process() -> Vec<String> {
    // Sequential awaits
    let a = fetch_data(1).await;
    let b = fetch_data(2).await;

    // Concurrent with tokio::join! — both run simultaneously
    let (c, d) = tokio::join!(fetch_data(3), fetch_data(4));

    vec![a, b, c, d]
}

// tokio::spawn — background task (like thread::spawn but async)
async fn concurrent_tasks() {
    let handle1 = tokio::spawn(fetch_data(10));
    let handle2 = tokio::spawn(fetch_data(20));
    let (r1, r2) = tokio::join!(handle1, handle2);
    println!("{}  {}", r1.unwrap(), r2.unwrap());
}

// tokio::select! — race multiple futures, take first to complete
async fn race_example() {
    tokio::select! {
        v = fetch_data(1) => println!("first: {}", v),
        v = fetch_data(2) => println!("first: {}", v),
    }
}

// Async channels
async fn channel_example() {
    let (tx, mut rx) = mpsc::channel::<i32>(32);
    tokio::spawn(async move {
        for i in 0..5 { tx.send(i).await.unwrap(); }
    });
    while let Some(msg) = rx.recv().await { println!("received: {}", msg); }
}

// Error handling in async
async fn fallible() -> Result<String, Box<dyn std::error::Error>> {
    let data = fetch_data(1).await;
    Ok(data.to_uppercase())
}

#[tokio::main]
async fn main() {
    let results = process().await;
    println!("{:?}", results);
    concurrent_tasks().await;
    race_example().await;
    channel_example().await;
    println!("{:?}", fallible().await);
}

Macros

rust
// Declarative macros — macro_rules!
macro_rules! say_hello {
    () => { println!("Hello!"); };
    ($name:expr) => { println!("Hello, {}!", $name); };
}

// Macro that creates a HashMap
macro_rules! map {
    ($($key:expr => $val:expr),* $(,)?) => {{
        let mut m = std::collections::HashMap::new();
        $( m.insert($key, $val); )*
        m
    }};
}

// Variadic macro
macro_rules! max {
    ($x:expr) => { $x };
    ($x:expr, $($rest:expr),+) => {{
        let rest_max = max!($($rest),+);
        if $x > rest_max { $x } else { rest_max }
    }};
}

fn main() {
    say_hello!();
    say_hello!("Alice");

    let m = map!{ 'a' => 1, 'b' => 2, 'c' => 3 };
    println!("{:?}", m);

    println!("max: {}", max!(3, 1, 4, 1, 5, 9, 2, 6));   // 9

    // Built-in macros
    println!("formatted {}", 42);        // print to stdout with newline
    eprintln!("error {}", "oops");        // print to stderr
    let s = format!("hello {}", "world");
    let v = vec![1, 2, 3];
    assert!(1 + 1 == 2);
    assert_eq!(2 + 2, 4, "math is broken");
    assert_ne!(1, 2);
    let x: Option<i32> = None;
    // panic!("something went wrong");   // explicit panic

    // dbg! — prints file/line/value, returns the value
    let a = dbg!(2 + 3) * dbg!(4);   // [src/main.rs:N] 2 + 3 = 5 ...
    println!("a={}", a);

    // todo!, unimplemented!, unreachable!
    // fn stub() -> i32 { todo!() }      // panics with "not yet implemented"

    // include_str!, include_bytes! — embed files at compile time
    // const DATA: &str = include_str!("../data.txt");

    // concat!, stringify!, env!, cfg!
    let s2 = concat!("foo", "bar", 42);   // "foobar42"
    let is_debug = cfg!(debug_assertions);
    println!("s2={} debug={}", s2, is_debug);
}

Smart Pointers

rust
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::Arc;

// Box<T> — heap allocation, single owner
fn main() {
    let b = Box::new(5);       // 5 stored on heap
    println!("b = {}", b);    // auto-deref

    // Box enables recursive types (otherwise infinite size)
    #[derive(Debug)]
    enum List { Cons(i32, Box<List>), Nil }
    let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
    println!("{:?}", list);

    // Rc<T> — reference-counted, single-threaded shared ownership
    let a = Rc::new(vec![1, 2, 3]);
    let b = Rc::clone(&a);   // increments ref count (strong)
    let c = Rc::clone(&a);
    println!("count={} a={:?}", Rc::strong_count(&a), a);
    drop(c);
    println!("count after drop={}", Rc::strong_count(&a));  // 2

    // RefCell<T> — interior mutability (borrow rules checked at runtime)
    let data = RefCell::new(vec![1, 2, 3]);
    {
        let mut borrow = data.borrow_mut();   // panics if already borrowed
        borrow.push(4);
    }
    println!("{:?}", data.borrow());   // [1, 2, 3, 4]

    // Rc<RefCell<T>> — shared mutable state in single-threaded code
    let shared = Rc::new(RefCell::new(0));
    let clone1 = Rc::clone(&shared);
    let clone2 = Rc::clone(&shared);
    *clone1.borrow_mut() += 10;
    *clone2.borrow_mut() += 20;
    println!("shared: {}", shared.borrow());   // 30

    // Arc<T> — atomic ref count, thread-safe version of Rc
    // Use Arc<Mutex<T>> for shared mutable state across threads (see concurrency section)
    let arc = Arc::new(vec![1, 2, 3]);
    let arc2 = Arc::clone(&arc);
    std::thread::spawn(move || println!("thread: {:?}", arc2)).join().unwrap();

    // Weak<T> — non-owning reference (prevents reference cycles)
    use std::rc::Weak;
    let strong = Rc::new(5);
    let weak: Weak<i32> = Rc::downgrade(&strong);
    println!("weak: {:?}", weak.upgrade());   // Some(5)
    drop(strong);
    println!("weak after drop: {:?}", weak.upgrade());   // None
}

Advanced Traits

rust
use std::fmt;

// Trait objects — dynamic dispatch via vtable
trait Draw { fn draw(&self); }

struct Screen { components: Vec<Box<dyn Draw>> }
impl Screen {
    fn render(&self) { for c in &self.components { c.draw(); } }
}

struct Button { label: String }
struct Image  { src: String }
impl Draw for Button { fn draw(&self) { println!("Button: {}", self.label); } }
impl Draw for Image  { fn draw(&self) { println!("Image: {}", self.src); } }

// Associated types — cleaner than generic parameters when there is one natural type
trait Converter {
    type Output;
    fn convert(&self) -> Self::Output;
}
struct Fahrenheit(f64);
impl Converter for Fahrenheit {
    type Output = f64;
    fn convert(&self) -> f64 { (self.0 - 32.0) * 5.0 / 9.0 }
}

// Trait with where clause
trait Printable where Self: fmt::Display + fmt::Debug {}
impl<T: fmt::Display + fmt::Debug> Printable for T {}

// Supertrait — require another trait
trait Animal: fmt::Display {
    fn name(&self) -> &str;
    fn sound(&self) -> &str;
    fn description(&self) -> String { format!("{} says {}", self.name(), self.sound()) }
}

// Blanket implementations — implement trait for all types that satisfy bounds
trait DoubleDisplay: fmt::Display {
    fn double_display(&self) -> String { format!("{}{}", self, self) }
}
impl<T: fmt::Display> DoubleDisplay for T {}   // auto-impl for all Display types

// Newtype pattern for orphan rule
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

fn main() {
    let screen = Screen {
        components: vec![
            Box::new(Button { label: String::from("OK") }),
            Box::new(Image  { src: String::from("logo.png") }),
        ]
    };
    screen.render();

    println!("celsius: {:.1}", Fahrenheit(212.0).convert());   // 100.0

    let nums = vec![String::from("a"), String::from("b")];
    let w = Wrapper(nums);
    println!("{}", w);   // [a, b]
    println!("double: {}", 42.double_display());   // 4242
}

Pattern Matching

rust
fn main() {
    // Destructuring structs
    struct Point { x: i32, y: i32 }
    let p = Point { x: 3, y: -10 };
    let Point { x, y } = p;
    println!("x={} y={}", x, y);

    // Destructuring enums
    enum Message {
        Move { x: i32, y: i32 },
        Write(String),
        Color(u8, u8, u8),
        Quit,
    }
    let msg = Message::Move { x: 10, y: 20 };
    match msg {
        Message::Move { x, y }  => println!("move to {},{}", x, y),
        Message::Write(text)     => println!("write: {}", text),
        Message::Color(r, g, b)  => println!("color {},{},{}", r, g, b),
        Message::Quit            => println!("quit"),
    }

    // Nested destructuring
    let ((a, b), c) = ((1, 2), 3);

    // Tuple in match
    let pair = (true, 42);
    match pair {
        (true, n) if n > 0 => println!("positive true: {}", n),
        (false, _)          => println!("false"),
        _                   => println!("other"),
    }

    // Range patterns
    let n = 13;
    match n {
        1..=12  => println!("less than 13"),
        13      => println!("thirteen"),
        14..=i32::MAX => println!("big"),
        _       => println!("negative"),
    }

    // @ bindings — capture and test
    let num = 7;
    match num {
        n @ 1..=10 => println!("1..=10: n={}", n),
        n @ 11..   => println!("big: n={}", n),
        _           => println!("other"),
    }

    // if let — single pattern (ignores non-matching)
    let config_max = Some(3u8);
    if let Some(max) = config_max { println!("max={}", max); }

    // if let + else
    let val: Result<i32, &str> = Ok(42);
    if let Ok(n) = val { println!("ok: {}", n); } else { println!("error"); }

    // while let
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() { println!("popped: {}", top); }

    // Ignore with ..
    struct Point3 { x: i32, y: i32, z: i32 }
    let p3 = Point3 { x: 1, y: 2, z: 3 };
    let Point3 { x, .. } = p3;   // ignore y and z

    // Multiple patterns with |
    let c = 'a';
    match c {
        'a' | 'e' | 'i' | 'o' | 'u' => println!("vowel"),
        'a'..='z'                    => println!("consonant"),
        _                            => println!("other"),
    }
}

Testing

rust
// Unit tests live in the same file, in a test module
// Run: cargo test

pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 { None } else { Some(a / b) }
}
pub fn greet(name: &str) -> String { format!("Hello, {}!", name) }

#[cfg(test)]   // compiled only during "cargo test"
mod tests {
    use super::*;   // import everything from parent module

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
    }

    #[test]
    fn test_divide() {
        assert_eq!(divide(10.0, 2.0), Some(5.0));
        assert_eq!(divide(1.0, 0.0), None);
    }

    #[test]
    fn test_greet() {
        let result = greet("Alice");
        assert!(result.contains("Alice"));
        assert_eq!(result, "Hello, Alice!");
        assert_ne!(result, "Hello, Bob!");
    }

    #[test]
    #[should_panic(expected = "divide by zero")]
    fn test_panics() {
        panic!("divide by zero");
    }

    #[test]
    fn test_result() -> Result<(), String> {
        // Tests can return Result — Err causes failure
        let n: i32 = "42".parse().map_err(|e| format!("parse error: {}", e))?;
        assert_eq!(n, 42);
        Ok(())
    }

    #[test]
    #[ignore = "slow test, run with --include-ignored"]
    fn test_slow() {
        std::thread::sleep(std::time::Duration::from_secs(2));
    }
}

// Integration tests go in tests/ directory (separate crate, test public API only)
// tests/integration_test.rs:
//   use my_crate::add;
//   #[test]
//   fn it_adds() { assert_eq!(add(2, 2), 4); }

// Doc tests — code in /// doc comments is compiled and run
/// Adds two numbers.
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add_doc(a: i32, b: i32) -> i32 { a + b }

// cargo test -- --nocapture     (show println! output)
// cargo test -- --test-threads=1  (sequential)
// cargo test test_add           (filter by name)

Cargo & Tooling

rust
# Cargo.toml — project manifest

[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
authors = ["Alice <alice@example.com>"]
description = "A sample Rust project"
license = "MIT"

[dependencies]
# Specific version
serde = "1.0"
# Version range
tokio = ">= 1.0, < 2.0"
# Git dependency
# my_lib = { git = "https://github.com/user/my_lib", branch = "main" }
# Path dependency (local)
# utils = { path = "../utils" }
# Optional dependency (enabled by features)
# reqwest = { version = "0.11", optional = true }

[dependencies.serde]
version = "1.0"
features = ["derive"]    # enable serde macros

[dev-dependencies]       # only for tests and benchmarks
criterion = "0.5"

[build-dependencies]     # only for build.rs
cc = "1.0"

[features]
default = ["std"]
std = []
async = ["tokio"]
full = ["std", "async"]

[profile.dev]
opt-level = 0    # fast compile, slow binary
debug = true

[profile.release]
opt-level = 3    # slow compile, fast binary
lto = true       # link-time optimisation
strip = true     # strip debug symbols

# Workspace — monorepo with multiple crates
# [workspace]
# members = ["crate_a", "crate_b", "crate_c"]

# --- Common cargo commands ---
# cargo new my_project --bin   create binary project
# cargo new my_lib --lib       create library project
# cargo build                  compile debug
# cargo build --release        compile release
# cargo run                    compile + run
# cargo run -- arg1 arg2       pass args to binary
# cargo test                   run all tests
# cargo test -- --nocapture    show stdout
# cargo check                  type-check without linking (fast)
# cargo clippy                 linter — catches common mistakes
# cargo fmt                    auto-format code (rustfmt)
# cargo doc --open             build + open API docs
# cargo add serde --features derive   add dependency
# cargo update                 update Cargo.lock
# cargo tree                   visualise dependency tree
# cargo bench                  run benchmarks
# cargo publish                publish to crates.io

Best Practices

Ownership & Borrowing

rust
// Prefer borrowing over cloning — avoid unnecessary heap allocations
fn process(data: &[i32]) -> i32 {        // borrow slice, not Vec
    data.iter().sum()
}

fn get_name(user: &User) -> &str {        // return borrow, not owned String
    &user.name
}

// Use owned types in structs; borrow in function signatures
struct Config { host: String, port: u16 }   // owns its data

// Split borrows — borrow disjoint fields simultaneously
struct Counter { value: i32, step: i32 }
impl Counter {
    fn increment(&mut self) {
        let step = self.step;        // copy before mutable borrow
        self.value += step;
    }
}

// Avoid fighting the borrow checker — restructure rather than using unsafe
// BAD pattern (compile error):
// fn bad(v: &mut Vec<i32>) -> &i32 { v.push(1); &v[0] }   // push invalidates ref

// GOOD: separate the borrow from the mutation
fn good(v: &mut Vec<i32>) -> i32 {
    v.push(1);
    v[0]   // return copy
}

// Use indices instead of references when mutating a collection you're iterating
fn remove_negatives(v: &mut Vec<i32>) {
    v.retain(|&x| x >= 0);   // retain is idiomatic
}

// Cow<str> for "sometimes owned, sometimes borrowed" data
use std::borrow::Cow;
fn maybe_uppercase<"a>(s: &"a str, upper: bool) -> Cow<'a, str> {
    if upper { Cow::Owned(s.to_uppercase()) }
    else { Cow::Borrowed(s) }
}

Error Handling

rust
use std::fmt;

// Define domain error types — avoid String errors
#[derive(Debug)]
pub enum ServiceError {
    NotFound(String),
    Unauthorized,
    Database(sqlx::Error),    // wrap third-party errors
}
impl fmt::Display for ServiceError { /* ... */ }
impl std::error::Error for ServiceError {}

// Use thiserror crate for less boilerplate
// [dependencies] thiserror = '1'
// #[derive(thiserror::Error, Debug)]
// pub enum MyError {
//     #[error("not found: {0}")] NotFound(String),
//     #[error("db error: {0}")] Db(#[from] sqlx::Error),
// }

// Use anyhow for application code (vs. library code)
// [dependencies] anyhow = '1'
// fn run() -> anyhow::Result<()> {
//     let n: i32 = "42".parse().context("parsing failed")?;
//     Ok(())
// }

// Never panic in library code — return Result or Option instead
// Reserve unwrap() for:
//   1. Tests
//   2. Truly impossible cases (document WHY it cannot fail)
//   3. Prototypes

// Propagate errors with ? — keeps happy path clean
fn load_and_parse(path: &str) -> Result<Config, Box<dyn std::error::Error>> {
    let contents = std::fs::read_to_string(path)?;
    let config: Config = serde_json::from_str(&contents)?;
    Ok(config)
}

// Centralize error conversion at crate boundaries
// Internal functions can return specific errors
// Public API returns a unified error type

// Use map_err to provide context
fn parse_port(s: &str) -> Result<u16, String> {
    s.parse::<u16>().map_err(|e| format!("invalid port {}: {}", s, e))
}
struct Config;

Traits & Generics

rust
// Prefer impl Trait in function signatures over Box<dyn Trait> when possible
// impl Trait = static dispatch (monomorphized, zero overhead)
// Box<dyn Trait> = dynamic dispatch (vtable, heap alloc)
fn process_static(iter: impl Iterator<Item = i32>) -> i32 { iter.sum() }
fn process_dynamic(iter: Box<dyn Iterator<Item = i32>>) -> i32 { iter.sum() }

// Use trait bounds to constrain generics precisely
use std::fmt;
fn print_twice<T: fmt::Display>(val: &T) {
    println!("{}", val);
    println!("{}", val);
}

// Prefer associated types over generic parameters for unique relationships
trait Container {
    type Item;    // there is only one natural Item type per Container
    fn first(&self) -> Option<&Self::Item>;
}

// Use Default trait for sensible zero-values
#[derive(Debug, Default)]
struct Config { timeout: u32, retries: u8, verbose: bool }
let c = Config { verbose: true, ..Config::default() };

// Blanket implementations extend functionality without modifying types
trait Summary { fn summary(&self) -> String; }
// implement for all Vecs whose elements implement Display
impl<T: fmt::Display> Summary for Vec<T> {
    fn summary(&self) -> String {
        self.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")
    }
}

// Derive common traits — less manual implementation
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct UserId(u64);
// Now usable as HashMap key, can be cloned, printed with {:?}, compared with ==

Concurrency

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

// Prefer message passing over shared state (channels are often simpler)
use std::sync::mpsc;
fn producer_consumer() {
    let (tx, rx) = mpsc::channel::<String>();
    let producer = thread::spawn(move || {
        for i in 0..10 { tx.send(format!("item-{}", i)).unwrap(); }
    });
    let consumer = thread::spawn(move || {
        for msg in rx { println!("consumed: {}", msg); }
    });
    producer.join().unwrap();
    consumer.join().unwrap();
}

// Minimise lock contention — hold locks for the shortest possible time
fn increment_counter(counter: &Arc<Mutex<i32>>) {
    let mut guard = counter.lock().unwrap();
    *guard += 1;
    // guard dropped here — NOT after long computation
}

// Avoid deadlocks — always acquire locks in the same order
// Consider using a single struct with multiple fields to avoid multiple locks

// Use Rayon for data parallelism (CPU-bound)
// [dependencies] rayon = '1'
// use rayon::prelude::*;
// let sum: i32 = (0..1_000_000).into_par_iter().sum();

// Use atomic types for simple counters (faster than Mutex)
use std::sync::atomic::{AtomicUsize, Ordering};
static REQUEST_COUNT: AtomicUsize = AtomicUsize::new(0);
fn handle_request() { REQUEST_COUNT.fetch_add(1, Ordering::Relaxed); }
fn get_request_count() -> usize { REQUEST_COUNT.load(Ordering::SeqCst) }

// Send + Sync bounds ensure thread-safety at compile time
// If your type contains only Send + Sync fields, it auto-derives them
// Arc<Mutex<T>> is Send + Sync even when T is not Sync alone

Performance

rust
// Profile before optimizing — cargo flamegraph, cargo bench (criterion)

// Pre-allocate collections when size is known
fn build_vec(n: usize) -> Vec<i32> {
    let mut v = Vec::with_capacity(n);   // single allocation
    for i in 0..n as i32 { v.push(i); }
    v
}

// Use iterators — they compose and often get optimised to tight loops
fn sum_squares(data: &[f64]) -> f64 {
    data.iter().map(|&x| x * x).sum()
}

// Avoid allocations in hot paths — prefer &str over String, slices over Vecs
fn count_vowels(s: &str) -> usize {
    s.chars().filter(|c| "aeiouAEIOU".contains(*c)).count()
}

// String building — use a reusable buffer
fn build_csv(rows: &[Vec<String>]) -> String {
    let mut out = String::new();   // or with_capacity estimate
    for row in rows {
        out.push_str(&row.join(','));
        out.push('\n');
    }
    out
}

// Use #[inline] for small frequently-called functions
#[inline(always)]
fn fast_max(a: i32, b: i32) -> i32 { if a > b { a } else { b } }

// Release profile optimizations (Cargo.toml)
// [profile.release]
// opt-level = 3
// lto = "fat"     -- link-time optimization across crates
// codegen-units = 1  -- single codegen unit (slower compile, faster binary)

// Avoid unnecessary Box/Rc/Arc — prefer stack values
// Use SmallVec (smallvec crate) when Vec usually has <= N elements
// Use ahash/FxHashMap instead of HashMap when hash quality matters less than speed

Tooling & Ecosystem

rust
# Essential toolchain commands

# Install / manage Rust versions
rustup update stable         # update stable toolchain
rustup show                  # list installed toolchains
rustup default nightly       # switch default to nightly
rustup override set 1.70.0   # pin version for current directory
rustup target add wasm32-unknown-unknown   # cross-compile target
rustup component add clippy rustfmt rust-analyzer

# Linting and formatting (enforce in CI)
cargo clippy -- -D warnings           # fail on any warning
cargo clippy --fix                    # auto-fix some lints
cargo fmt                             # format code
cargo fmt -- --check                  # verify (CI mode)

# Useful Clippy lints to enable in code or .clippy.toml
// #![deny(clippy::all)]
// #![warn(clippy::pedantic)]
// #![allow(clippy::too_many_arguments)]

# Dependency management
cargo outdated         # list outdated deps (cargo-outdated)
cargo audit            # check for security advisories (cargo-audit)
cargo tree             # visualise dependency tree
cargo machete          # find unused dependencies
cargo deny check       # license + advisory policy (cargo-deny)

# Documentation
cargo doc --no-deps --open   # build own crate's docs
/// Three-slash comments produce doc pages
/// ```
/// # Examples
/// let x = my_crate::add(1, 2);
/// assert_eq!(x, 3);
/// ```

# Recommended crates by category
# Error handling:  thiserror (libraries), anyhow (binaries)
# Async:          tokio, async-std
# HTTP client:    reqwest
# Serialization:  serde + serde_json / serde_yaml / bincode
# CLI:            clap (derive feature), argh
# Logging:        tracing, log + env_logger
# Testing:        pretty_assertions, mockall, insta (snapshots)
# Databases:      sqlx (async, compile-time checked queries)
# Data parallel:  rayon