📋 Cheat Sheets

Rust Cheat Sheet — Ownership, Syntax, and Common Patterns


Click any item to expand the explanation and examples.

📝 Basics

Variables and types basics
// Immutable by default
let name = "Alice";
let age: i32 = 30;

// Mutable let mut count = 0; count += 1;

// Constants const MAX_SIZE: usize = 100;

// Shadowing (re-declare same name) let x = 5; let x = x + 1; // x is now 6 let x = “hello”; // x is now a string (different type!)

// Types // i8, i16, i32, i64, i128, isize // u8, u16, u32, u64, u128, usize // f32, f64 // bool, char // &str (string slice), String (owned string) // () — unit type (like void)

// Type conversion let x: i32 = 42; let y: f64 = x as f64;

Functions basics
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

// Last expression is the return value (no semicolon) fn add(a: i32, b: i32) -> i32 { a + b }

// No return value fn log(msg: &str) { println!(”{}”, msg); }

// Closures let double = |x: i32| x * 2; let result = double(5); // 10

// Closure capturing environment let offset = 10; let add_offset = |x| x + offset;

🔑 Ownership & Borrowing

Ownership rules ownership
The core concept that makes Rust unique.
// Rule 1: Each value has one owner
let s1 = String::from("hello");
let s2 = s1;       // s1 is MOVED to s2
// println!("{}", s1);  // ❌ Error! s1 is no longer valid

// Rule 2: Clone to copy heap data let s1 = String::from(“hello”); let s2 = s1.clone(); // Deep copy println!(”{} {}”, s1, s2); // ✅ Both valid

// Rule 3: Stack types (i32, bool, f64, char) are copied automatically let x = 5; let y = x; // Copy, not move println!(”{} {}”, x, y); // ✅ Both valid

// Ownership and functions fn take_ownership(s: String) { println!(”{}”, s); } // s is dropped here

let s = String::from(“hello”); take_ownership(s); // println!(”{}”, s); // ❌ s was moved into the function

Borrowing — & and &mut ownership
// Immutable borrow (can have many)
fn print_len(s: &String) {
    println!("Length: {}", s.len());
}

let s = String::from(“hello”); print_len(&s); // Borrow println!(”{}”, s); // ✅ Still valid

// Mutable borrow (can have only ONE at a time) fn add_world(s: &mut String) { s.push_str(”, world!”); }

let mut s = String::from(“hello”); add_world(&mut s); println!(”{}”, s); // “hello, world!”

// Rules: // - Many &T OR one &mut T (never both) // - References must always be valid (no dangling pointers)

📦 Structs & Enums

Structs data
struct User {
    name: String,
    email: String,
    age: u32,
}

// Create let user = User { name: String::from(“Alice”), email: String::from(“alice@example.com”), age: 30, };

// Access println!(”{}”, user.name);

// Methods impl User { // Constructor (convention: fn new) fn new(name: &str, email: &str) -> Self { User { name: name.to_string(), email: email.to_string(), age: 0, } }

// Method (takes &self)
fn greeting(&self) -> String {
    format!("Hi, I'm {}", self.name)
}

// Mutable method
fn set_age(&mut self, age: u32) {
    self.age = age;
}

}

let mut user = User::new(“Alice”, “alice@example.com”); user.set_age(30);

Enums and pattern matching data
enum Color {
    Red,
    Green,
    Blue,
    Custom(u8, u8, u8),  // With data
}

let c = Color::Custom(255, 128, 0);

// Pattern matching (must be exhaustive) match c { Color::Red => println!(“Red”), Color::Green => println!(“Green”), Color::Blue => println!(“Blue”), Color::Custom(r, g, b) => println!(“RGB({},{},{})”, r, g, b), }

// if let (match one pattern) if let Color::Custom(r, _, _) = c { println!(“Red component: {}”, r); }

// Option — Rust’s null replacement let x: Option<i32> = Some(42); let y: Option<i32> = None;

match x { Some(val) => println!(“Got {}”, val), None => println!(“Nothing”), }

// Unwrap shortcuts x.unwrap() // Panics if None x.unwrap_or(0) // Default value x.unwrap_or_default() // Type’s default

⚠️ Error Handling

Result and the ? operator errors
use std::fs;
use std::io;

// Functions return Result<T, E> fn read_file(path: &str) -> Result<String, io::Error> { fs::read_to_string(path) }

// Handle with match match read_file(“config.txt”) { Ok(content) => println!(”{}”, content), Err(e) => eprintln!(“Error: {}”, e), }

// The ? operator — propagate errors fn read_config() -> Result<String, io::Error> { let content = fs::read_to_string(“config.txt”)?; // Returns Err early if fails Ok(content.trim().to_string()) }

// Custom error #[derive(Debug)] enum AppError { Io(io::Error), Parse(String), }

impl From<io::Error> for AppError { fn from(e: io::Error) -> Self { AppError::Io(e) } }

// Or use anyhow crate for simple error handling // use anyhow::{Result, Context}; // fn main() -> Result<()> { // let f = fs::read_to_string(“file.txt”) // .context(“Failed to read config”)?; // Ok(()) // }

🔌 Traits

Traits — Rust's interfaces traits
trait Summary {
    fn summarize(&self) -> String;
// Default implementation
fn preview(&self) -> String {
    format!("{}...", &self.summarize()[..20])
}

}

struct Article { title: String, content: String, }

impl Summary for Article { fn summarize(&self) -> String { format!(”{}: {}”, self.title, &self.content[..50]) } }

// Trait as parameter fn notify(item: &impl Summary) { println!(“Breaking: {}”, item.summarize()); }

// Trait bound syntax (equivalent) fn notify<T: Summary>(item: &T) { println!(“Breaking: {}”, item.summarize()); }

// Common derive traits #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Point { x: i32, y: i32, }

📋 Collections

Vec, HashMap, iterators collections
// Vec
let mut v = vec![1, 2, 3];
v.push(4);
v.pop();
v.len();
v[0];                    // Panics if out of bounds
v.get(0);                // Returns Option

// HashMap use std::collections::HashMap; let mut map = HashMap::new(); map.insert(“key”, “value”); map.get(“key”); // Returns Option map.contains_key(“key”); map.remove(“key”);

// Iterators (Rust’s superpower) let nums = vec![1, 2, 3, 4, 5];

let doubled: Vec<i32> = 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(); let any_big = nums.iter().any(|x| *x > 3); let all_pos = nums.iter().all(|x| *x > 0); let found = nums.iter().find(|x| **x == 3);

// Chain let result: Vec<i32> = nums.iter() .filter(|x| **x > 2) .map(|x| x * 10) .collect();

🔧 Cargo Commands

cargo — project management cli
# Create project
cargo new my-project
cargo new my-lib --lib

Build and run

cargo build cargo build —release cargo run cargo run —release

Test

cargo test cargo test test_name cargo test — —nocapture # Show println output

Check (fast compile check, no binary)

cargo check

Format and lint

cargo fmt cargo clippy

Add dependency

cargo add serde cargo add tokio —features full

Update dependencies

cargo update

Documentation

cargo doc —open