🦀 Systems Programming
Rust Complete Cheatsheet
Ownership, borrowing, structs, traits, Result/Option and Cargo — complete Rust reference.
📖 10 sections
⏱ 26 min read
✅ Quizzes included
🌙 Dark mode
01 Ownership & Borrowing
RUSTOwnership — Rust's core concept
// Rule 1: Each value has one owner
// Rule 2: When owner goes out of scope, value is dropped
// Rule 3: Can't have two owners at once

let s1 = String::from("hello");
let s2 = s1;           // s1 MOVED to s2 — s1 no longer valid!
// println!("{}", s1); // ERROR: value moved

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

// Borrowing — references (&)
fn calculate_length(s: &String) -> usize {
    s.len()  // borrow, don't take ownership
}
let len = calculate_length(&s2);  // pass reference

// Mutable reference
fn change(s: &mut String) { s.push_str(", world"); }
let mut s4 = String::from("hello");
change(&mut s4);

// RULES: only ONE mutable reference at a time
// OR multiple immutable references — never both together
💡
Rust's ownership prevents memory leaks and data races AT COMPILE TIME. No garbage collector needed.
02 Data Types
RUSTTypes and variables
// Scalar types
let x: i32 = -42;          // i8 i16 i32 i64 i128 isize
let y: u64 = 9999999;       // u8 u16 u32 u64 u128 usize
let f: f64 = 3.14;          // f32 f64
let b: bool = true;
let c: char = '🦀';          // Unicode scalar value

// Compound types
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (a, b, c) = tup;         // destructuring
let first = tup.0;           // index access

let arr: [i32; 5] = [1, 2, 3, 4, 5];  // fixed size!
let arr2 = [3; 5];           // [3, 3, 3, 3, 3]
let slice = &arr[1..3];      // &[i32] slice

// String types
let s_lit: &str = "hello";           // string slice — stack
let s_own: String = String::from("world"); // owned — heap
let combined = format!("{} {}", s_lit, s_own);

// Shadowing
let spaces = "   ";          // &str
let spaces = spaces.len();   // usize — shadows previous!
03 Functions & Closures
RUSTFunctions and closures
fn add(a: i32, b: i32) -> i32 { a + b }  // implicit return (no semicolon)

fn largest(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list { if item > largest { largest = item; } }
    largest
}

// Closures — anonymous functions
let double = |x| x * 2;
let add_n = |n| move |x| x + n;  // capture by move
let five_doubled = double(5);     // 10

// Higher-order functions
let nums = vec![1, 2, 3, 4, 5];
let doubled: Vec = nums.iter().map(|&x| x * 2).collect();
let evens: Vec<&i32> = nums.iter().filter(|&&x| x % 2 == 0).collect();
let sum: i32 = nums.iter().sum();
04 Structs & Enums
RUSTStructs and Enums
// Struct
#[derive(Debug, Clone)]
struct User {
    name: String,
    email: String,
    age: u32,
    active: bool,
}

impl User {
    fn new(name: &str, email: &str) -> User {
        User { name: String::from(name), email: String::from(email), age: 0, active: true }
    }
    fn greeting(&self) -> String { format!("Hi, I'm {}", self.name) }
    fn make_inactive(&mut self) { self.active = false; }
}

// Enum (more powerful than C enums)
#[derive(Debug)]
enum Shape {
    Circle(f64),            // radius
    Rectangle(f64, f64),    // width, height
    Triangle { base: f64, height: f64 },  // named fields
}

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 { base, height } => 0.5 * base * height,
        }
    }
}
05 Pattern Matching
RUSTPattern matching
// match — exhaustive!
let x = 5;
match x {
    1 => println!("one"),
    2 | 3 => println!("two or three"),
    4..=9 => println!("four to nine"),
    _ => println!("other"),
}

// if let — for single pattern
let some_val: Option = Some(42);
if let Some(n) = some_val { println!("Got: {}", n); }

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

// Destructuring in patterns
let (a, b, c) = (1, 2, 3);
let Point { x, y } = p;
let [first, .., last] = arr.as_slice();

// Guards
match x {
    n if n < 0 => println!("negative"),
    n if n % 2 == 0 => println!("positive even"),
    _ => println!("positive odd"),
}
06 Error Handling
RUSTError handling with Result and Option
use std::num::ParseIntError;

// Option — value may be absent
fn divide(a: f64, b: f64) -> Option {
    if b == 0.0 { None } else { Some(a / b) }
}
match divide(10.0, 2.0) {
    Some(result) => println!("Result: {}", result),
    None => println!("Cannot divide by zero"),
}
divide(10.0, 2.0).unwrap_or(0.0);    // default on None
divide(10.0, 2.0).unwrap();          // panic on None!

// Result — value or error
fn parse_num(s: &str) -> Result {
    s.parse::()
}
match parse_num("42") {
    Ok(n) => println!("Parsed: {}", n),
    Err(e) => println!("Error: {}", e),
}

// ? operator — propagate errors
fn read_number(s: &str) -> Result {
    let n = s.trim().parse::()?;  // return early if Err
    Ok(n * 2)
}
💡
Use ? operator to propagate errors. Avoid .unwrap() in production — it panics on None/Err.
07 Traits
RUSTTraits — Rust's interfaces
// Define trait
trait Summary {
    fn summarise(&self) -> String;
    fn preview(&self) -> String {  // default implementation
        format!("Read more: {}", self.summarise())
    }
}

// Implement trait
struct Article { title: String, content: String }
impl Summary for Article {
    fn summarise(&self) -> String {
        format!("{}: {}...", self.title, &self.content[..50])
    }
}

// Trait bounds (generics with constraints)
fn notify(item: &impl Summary) { println!("{}", item.summarise()); }
fn notify(item: &T) { println!("{}", item.summarise()); }
fn notify(item: &T) {}  // multiple bounds

// Common traits
// Display — fmt::Display (how to print)
// Debug — #[derive(Debug)] or manual
// Clone — #[derive(Clone)]
// Copy — types that copy by default (i32, f64, bool, char)
// Iterator — .next() -> Option
// From / Into — type conversions
08 Iterators & Closures
RUSTIterators
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Iterator adapters (lazy — don't run until consumed)
nums.iter()                    // &i32 iterator
nums.into_iter()               // owned i32 iterator
nums.iter_mut()                // &mut i32 iterator

// Chaining adapters
let result: Vec = nums.iter()
    .filter(|&&x| x % 2 == 0)  // keep even
    .map(|&x| x * x)           // square each
    .take(3)                    // only first 3
    .collect();                 // [4, 16, 36]

// Consuming adapters
nums.iter().sum::();      // 55
nums.iter().count();           // 10
nums.iter().max();             // Some(10)
nums.iter().any(|&x| x > 9);  // true
nums.iter().all(|&x| x > 0);  // true
nums.iter().position(|&x| x == 5);  // Some(4)

// zip
let a = vec![1,2,3];
let b = vec!['a','b','c'];
let zipped: Vec<_> = a.iter().zip(b.iter()).collect();

// enumerate
for (i, val) in nums.iter().enumerate() { println!("{}: {}", i, val); }
09 Cargo & Modules
RUSTCargo and modules
# Cargo commands
cargo new myproject        # create new project
cargo new --lib mylib      # create library
cargo build                # compile debug
cargo build --release      # optimized release build
cargo run                  # build and run
cargo test                 # run tests
cargo doc --open           # generate + open docs
cargo fmt                  # format code
cargo clippy               # lint
cargo add serde            # add dependency (modern)

# Cargo.toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }  # async runtime

# Modules
mod utils {
    pub fn greet(name: &str) { println!("Hello, {}!", name); }
    pub mod math { pub fn add(a: i32, b: i32) -> i32 { a + b } }
}
use utils::greet;
use utils::math::add;
10 Mini Quizzes
❓ Quiz 1
What happens when you assign one String to another variable in Rust?
Rust transfers ownership (moves) when assigning heap-allocated types like String. The original variable is invalidated. Use .clone() for a deep copy, or use references (&) to borrow without moving.
❓ Quiz 2
What does the ? operator do in a Result context?
The ? operator is shorthand for: if Err(e), return Err(e.into()). If Ok(v), continue with v. It propagates errors up the call stack cleanly. Can only be used in functions that return Result or Option.