Rust Cheatsheet

Quick reference for Rust basics — data structures, functions, control flow, ownership, and concurrency.

Data Structures

Structure Syntax Notes
Array [1, 2, 3] Fixed size, stack-allocated
Vec vec![1, 2, 3] Growable, heap-allocated
Tuple ("Ada", 30) Heterogeneous, fixed size
HashMap HashMap::new() Key-value, requires use
HashSet HashSet::new() Unique values
Slice &v[1..3] Borrowed view into a sequence

Functions

// Basic function
fn greet(name: &str) -> String {
    format!("Hello, {}", name)
}

// Multiple return values via tuple
fn divmod(a: i32, b: i32) -> (i32, i32) {
    (a / b, a % b)
}

// Ownership transfer (move)
fn take_ownership(s: String) {
    println!("{}", s);
} // s dropped here

// Borrow (reference)
fn borrow(s: &String) -> i32 {
    s.len() as i32
}

// Mutable borrow
fn modify(s: &mut String) {
    s.push_str("!");
}

// Generic function
fn first<T>(v: &[T]) -> Option<&T> {
    v.first()
}

// Closures
let square = |x: i32| x * x;
let greet = |name: &str| format!("Hi, {}", name);

Variables & Types

// Immutable by default
let x = 42;
let mut y = 42;       // mutable
y = 99;

// Type annotations
let name: &str = "Ada";
let age: u32 = 30;
let pi: f64 = 3.14159;

// Constants (must be typed, compile-time eval)
const MAX_SIZE: usize = 1024;

// Shadowing (reuse name with new type)
let x = 5;
let x = x * 2;       // x is now 10, can change type

// Type aliases
type Kilometers = i32;

Strings

let s: String = String::from("hello");    // owned, heap
let slice: &str = "hello";                 // borrowed, static

// Convert between them
let s2 = slice.to_string();               // &str -> String
let s3 = s.clone();                        // deep copy
let view: &str = &s;                        // String -> &str

// String operations
let msg = format!("Hello, {}!", name);
let msg = format!("{:?}, {:#?}", data, data); // debug format
let len = msg.len();
let bytes = msg.as_bytes();
let chars: Vec<char> = msg.chars().collect();

Option & Result

// Option<T> replaces null
let some: Option<i32> = Some(42);
let none: Option<i32> = None;

// Unwrapping (panics on None)
let val = some.unwrap();
let val = some.unwrap_or(0);
let val = some.unwrap_or_default();

// Pattern matching
match some {
    Some(v) => println!("{}", v),
    None => println!("nothing"),
}

// if let
if let Some(v) = some {
    println!("{}", v);
}

// Result<T, E> replaces exceptions
let result: Result<i32, &str> = Ok(42);
let err: Result<i32, &str> = Err("failed");

// The ? operator propagates errors
fn read_file(path: &str) -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    Ok(content)
}

Loops & Iterators

// for loop
for i in 0..5 {
    println!("{}", i);  // 0, 1, 2, 3, 4
}

// for over iterator
for item in vec.iter() { }
for item in vec.iter_mut() { }
for (i, item) in vec.iter().enumerate() { }

// while
let mut n = 0;
while n < 5 {
    n += 1;
}

// loop (infinite, break to exit)
let mut count = 0;
loop {
    count += 1;
    if count >= 5 { break; }
}

// Iterator methods (like JS array methods)
let doubled: Vec<i32> = nums.iter().map(|x| x * 2).collect();
let evens: Vec<i32> = nums.iter().filter(|x| *x % 2 == 0).copied().collect();
let sum: i32 = nums.iter().sum();
let has_any = nums.iter().any(|x| *x > 5);
let found = nums.iter().find(|x| **x == 3);
let (evens, odds): (Vec<i32>, Vec<i32>) = nums.iter()
    .partition(|x| *x % 2 == 0);

Pattern Matching

// Exhaustive match
enum Direction { Up, Down, Left, Right }
fn move_player(dir: Direction) {
    match dir {
        Direction::Up => println!("up"),
        Direction::Down => println!("down"),
        Direction::Left | Direction::Right => println!("horizontal"),
    }
}

// Destructuring enums with data
enum Shape {
    Circle { radius: f64 },
    Rect { w: f64, h: f64 },
}
fn area(s: Shape) -> f64 {
    match s {
        Shape::Circle { radius } => std::f64::consts::PI * radius * radius,
        Shape::Rect { w, h } => w * h,
    }
}

// Match guards
match x {
    n if n > 0 => println!("positive"),
    0 => println!("zero"),
    _ => println!("negative"),
}

Structs, Enums & Traits

// Struct
struct User {
    name: String,
    age: u32,
}

// Struct methods via impl
impl User {
    fn new(name: String, age: u32) -> Self {
        Self { name, age }
    }

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

    fn have_birthday(&mut self) {
        self.age += 1;
    }
}

// Trait (like interface)
trait Greets {
    fn greet(&self) -> String;
}

impl Greets for User {
    fn greet(&self) -> String {
        format!("Hi, I'm {}", self.name)
    }
}

// Enum with data
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Concurrency

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

// Spawning threads
let handle = thread::spawn(|| {
    "hello from thread"
});
let result = handle.join().unwrap();

// Channels (message passing)
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
    tx.send(42).unwrap();
});
let val = rx.recv().unwrap();

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

Cargo Commands

Command Purpose
cargo new myproject Create new project
cargo build Compile (debug)
cargo build --release Compile (optimized)
cargo run Build and run
cargo test Run tests
cargo check Type-check only (fast)
cargo fmt Format code
cargo clippy Lint code
cargo add <crate> Add dependency
cargo doc --open Generate and open docs