Rust Cheat Sheet
Quick Reference
Section titled “Quick Reference”| Concept | Syntax/Example |
|---|---|
| Immutable binding | let x = 5; |
| Mutable binding | let mut x = 5; |
| Borrow (immutable) | &x |
| Borrow (mutable) | &mut x |
| Lifetime annotation | 'a |
| Option handling | Some(x), None, .unwrap(), ? |
| Result handling | Ok(x), Err(e), .unwrap(), ? |
| Pattern match | match x { ... } |
| If let | if let Some(v) = opt { ... } |
| Derive traits | #[derive(Debug, Clone)] |
| Module declaration | mod name; |
| Public visibility | pub fn, pub struct, pub mod |
| Build project | cargo build |
| Run project | cargo run |
| Run tests | cargo test |
Ownership and Borrowing
Section titled “Ownership and Borrowing”Ownership Rules
Section titled “Ownership Rules”// 1. Each value has exactly one ownerlet s1 = String::from("hello");
// 2. Value is dropped when owner goes out of scope{ let s2 = String::from("world");} // s2 dropped here
// 3. Ownership transfers on assignment (move)let s3 = s1; // s1 moved to s3, s1 no longer valid// println!("{}", s1); // ERROR: value borrowed after move
// Clone to create a deep copylet s4 = s3.clone(); // s3 still validBorrowing Rules
Section titled “Borrowing Rules”let mut s = String::from("hello");
// Immutable borrows: unlimited simultaneous readerslet r1 = &s;let r2 = &s;println!("{} {}", r1, r2); // OK: multiple immutable borrows
// Mutable borrow: exclusive accesslet r3 = &mut s;r3.push_str(" world");
// Cannot mix mutable and immutable borrowslet r4 = &s;// let r5 = &mut s; // ERROR: cannot borrow as mutableReferences in Functions
Section titled “References in Functions”// Borrow instead of taking ownershipfn calculate_length(s: &String) -> usize { s.len()}
// Mutable borrow to modifyfn append(s: &mut String) { s.push_str(" world");}
let mut s = String::from("hello");let len = calculate_length(&s); // Immutable borrowappend(&mut s); // Mutable borrowLifetimes
Section titled “Lifetimes”Basic Syntax
Section titled “Basic Syntax”// Lifetime annotation on referencesfn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }}
// Multiple lifetimesfn first_word<'a, 'b>(s: &'a str, prefix: &'b str) -> &'a str { &s[..s.find(' ').unwrap_or(s.len())]}Struct Lifetimes
Section titled “Struct Lifetimes”// Struct holding a reference needs lifetimestruct Excerpt<'a> { part: &'a str,}
impl<'a> Excerpt<'a> { fn level(&self) -> i32 { 3 }
// Return type lifetime tied to self fn announce(&self, announcement: &str) -> &'a str { println!("Attention: {}", announcement); self.part }}Lifetime Elision
Section titled “Lifetime Elision”// Compiler infers lifetimes in common cases
// These are equivalent:fn first(s: &str) -> &str { &s[..1] }fn first_explicit<'a>(s: &'a str) -> &'a str { &s[..1] }
// Elision rules:// 1. Each input reference gets its own lifetime// 2. If one input lifetime, output gets that lifetime// 3. If &self or &mut self, output gets self's lifetimeStatic Lifetime
Section titled “Static Lifetime”// 'static: lives for entire program durationlet s: &'static str = "I live forever";
// String literals are always 'staticconst GREETING: &str = "Hello"; // Implicitly 'staticError Handling
Section titled “Error Handling”Option
Section titled “Option”let some_value: Option<i32> = Some(5);let no_value: Option<i32> = None;
// Pattern matchingmatch some_value { Some(v) => println!("Got: {}", v), None => println!("Nothing"),}
// Common methodssome_value.unwrap(); // Panic if Nonesome_value.unwrap_or(0); // Default if Nonesome_value.unwrap_or_default(); // Type's default if Nonesome_value.expect("msg"); // Panic with message if Nonesome_value.is_some(); // Returns boolsome_value.is_none(); // Returns bool
// Transformsome_value.map(|v| v * 2); // Some(10) or Nonesome_value.and_then(|v| Some(v * 2)); // Chainablesome_value.filter(|v| *v > 3); // Some if predicate true
// Convert to Resultsome_value.ok_or("error"); // Option -> ResultResult
Section titled “Result”use std::fs::File;use std::io::{self, Read};
fn read_file(path: &str) -> Result<String, io::Error> { let mut file = File::open(path)?; // ? propagates error let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}
// Pattern matchingmatch read_file("test.txt") { Ok(contents) => println!("{}", contents), Err(e) => eprintln!("Error: {}", e),}
// Common methodsresult.unwrap(); // Panic if Errresult.unwrap_or(default); // Default if Errresult.expect("msg"); // Panic with message if Errresult.is_ok(); // Returns boolresult.is_err(); // Returns boolresult.ok(); // Result -> Option (discards Err)result.err(); // Get Err as Option
// Transformresult.map(|v| v.len()); // Transform Ok valueresult.map_err(|e| CustomError::from(e)); // Transform ErrThe ? Operator
Section titled “The ? Operator”// ? returns early on Err/None, unwraps on Ok/Somefn process() -> Result<i32, String> { let x = may_fail()?; // Returns Err early if failed let y = may_fail()?; Ok(x + y)}
// Works with Option in functions returning Optionfn first_even(nums: &[i32]) -> Option<i32> { let first = nums.first()?; // Returns None if empty if first % 2 == 0 { Some(*first) } else { None }}
// Convert between Option and Result with ?fn combined() -> Result<i32, &'static str> { let opt: Option<i32> = Some(5); let val = opt.ok_or("was none")?; // Convert then propagate Ok(val)}Pattern Matching
Section titled “Pattern Matching”Match Expressions
Section titled “Match Expressions”let x = 5;
match x { 1 => println!("one"), 2 | 3 => println!("two or three"), // Multiple patterns 4..=6 => println!("four through six"), // Range n if n > 10 => println!("big: {}", n), // Guard _ => println!("other"), // Wildcard}
// Match returns a valuelet description = match x { 1 => "one", 2 => "two", _ => "many",};Destructuring
Section titled “Destructuring”// Tupleslet point = (3, 5);match point { (0, 0) => println!("origin"), (x, 0) => println!("on x-axis at {}", x), (0, y) => println!("on y-axis at {}", y), (x, y) => println!("at ({}, {})", x, y),}
// Structsstruct Point { x: i32, y: i32 }let p = Point { x: 0, y: 7 };
match p { Point { x: 0, y } => println!("on y-axis at {}", y), Point { x, y: 0 } => println!("on x-axis at {}", x), Point { x, y } => println!("at ({}, {})", x, y),}
// Enumsenum Message { Quit, Move { x: i32, y: i32 }, Write(String), Color(i32, i32, i32),}
match msg { Message::Quit => println!("quit"), Message::Move { x, y } => println!("move to {},{}", x, y), Message::Write(text) => println!("text: {}", text), Message::Color(r, g, b) => println!("rgb({},{},{})", r, g, b),}If Let and While Let
Section titled “If Let and While Let”// if let: match single patternlet opt = Some(5);if let Some(value) = opt { println!("Got {}", value);}
// With elseif let Some(v) = opt { println!("value: {}", v);} else { println!("no value");}
// while let: loop while pattern matcheslet mut stack = vec![1, 2, 3];while let Some(top) = stack.pop() { println!("{}", top);}
// let else: bind or divergefn process(opt: Option<i32>) -> i32 { let Some(value) = opt else { return 0; // Must diverge (return, panic, break, etc.) }; value * 2}Common Traits
Section titled “Common Traits”Derivable Traits
Section titled “Derivable Traits”#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]struct Point { x: i32, y: i32,}
// Debug: {:?} formattingprintln!("{:?}", point); // Point { x: 1, y: 2 }println!("{:#?}", point); // Pretty print
// Clone: explicit duplicationlet p2 = point.clone();
// Copy: implicit bitwise copy (requires Clone)let p3 = point; // point still valid
// PartialEq/Eq: == and != comparisonpoint == p2;
// Hash: use in HashMap/HashSetuse std::collections::HashMap;let mut map: HashMap<Point, &str> = HashMap::new();
// Default: default valuelet p4 = Point::default(); // Point { x: 0, y: 0 }Display and Debug
Section titled “Display and Debug”use std::fmt;
struct Point { x: i32, y: i32 }
// Debug: programmer-facing outputimpl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y) }}
// Display: user-facing outputimpl fmt::Display for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({}, {})", self.x, self.y) }}
let p = Point { x: 1, y: 2 };println!("{:?}", p); // Debug: Point { x: 1, y: 2 }println!("{}", p); // Display: (1, 2)From and Into
Section titled “From and Into”struct Meters(f64);struct Feet(f64);
// Implement From, get Into for freeimpl From<Feet> for Meters { fn from(feet: Feet) -> Self { Meters(feet.0 * 0.3048) }}
let feet = Feet(10.0);let meters: Meters = feet.into(); // Uses Intolet meters2 = Meters::from(Feet(10.0)); // Uses From
// From for error conversionimpl From<std::io::Error> for MyError { fn from(err: std::io::Error) -> Self { MyError::Io(err) }}// Now ? automatically converts io::Error to MyErrorClone vs Copy
Section titled “Clone vs Copy”// Copy: implicit, bitwise, stack-only// - Primitives (i32, f64, bool, char)// - Tuples/arrays of Copy types// - Immutable references (&T)
// Clone: explicit, potentially expensive// - Heap-allocated types (String, Vec, Box)// - Types with custom duplication logic
#[derive(Clone)] // Only Clonestruct Heap { data: Vec<i32> }
#[derive(Clone, Copy)] // Both (requires all fields be Copy)struct Stack { x: i32, y: i32 }Iterators and Closures
Section titled “Iterators and Closures”Iterator Basics
Section titled “Iterator Basics”let v = vec![1, 2, 3, 4, 5];
// Three ways to iteratefor x in v.iter() { } // Borrow: &Tfor x in v.iter_mut() { } // Mutable borrow: &mut Tfor x in v.into_iter() { } // Ownership: T (consumes v)
// for loop uses into_iter by defaultfor x in &v { } // Same as v.iter()for x in &mut v { } // Same as v.iter_mut()for x in v { } // Same as v.into_iter()Iterator Adaptors
Section titled “Iterator Adaptors”let v = vec![1, 2, 3, 4, 5];
// Transformv.iter().map(|x| x * 2); // [2, 4, 6, 8, 10]v.iter().filter(|x| *x % 2 == 0); // [2, 4]v.iter().filter_map(|x| { // Filter and map combined if *x > 2 { Some(x * 2) } else { None }});
// Flattenlet nested = vec![vec![1, 2], vec![3, 4]];nested.into_iter().flatten(); // [1, 2, 3, 4]v.iter().flat_map(|x| vec![*x, x * 2]); // Flatten after map
// Take/Skipv.iter().take(3); // First 3 elementsv.iter().skip(2); // Skip first 2v.iter().take_while(|x| **x < 4); // Take while predicatev.iter().skip_while(|x| **x < 3); // Skip while predicate
// Enumerate/Zipv.iter().enumerate(); // (index, value) pairsv.iter().zip(other.iter()); // Pair elementsConsuming Iterators
Section titled “Consuming Iterators”let v = vec![1, 2, 3, 4, 5];
// Collect into collectionlet doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();let set: HashSet<_> = v.iter().collect();
// Reducev.iter().sum::<i32>(); // 15v.iter().product::<i32>(); // 120v.iter().fold(0, |acc, x| acc + x); // Custom reductionv.iter().reduce(|a, b| a.max(b)); // Returns Option
// Findv.iter().find(|x| **x > 3); // Some(&4)v.iter().position(|x| *x > 3); // Some(3) (index)v.iter().any(|x| *x > 3); // truev.iter().all(|x| *x > 0); // true
// Count/Min/Maxv.iter().count(); // 5v.iter().min(); // Some(&1)v.iter().max(); // Some(&5)Closures
Section titled “Closures”// Type inferencelet add = |a, b| a + b;let add_explicit = |a: i32, b: i32| -> i32 { a + b };
// Capture modes (inferred by compiler)let x = 5;let borrow = || println!("{}", x); // Borrows xlet mut y = 5;let mut mutate = || y += 1; // Mutably borrows ylet z = String::from("hi");let consume = || drop(z); // Takes ownership of z
// Force move with move keywordlet s = String::from("hello");let closure = move || println!("{}", s);// s no longer accessible here
// Closure traits// Fn: borrows immutably, can call multiple times// FnMut: borrows mutably, can call multiple times// FnOnce: takes ownership, can call only once
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x)}Cargo Basics
Section titled “Cargo Basics”Common Commands
Section titled “Common Commands”cargo new project_name # Create new binary projectcargo new --lib lib_name # Create new librarycargo build # Build debugcargo build --release # Build optimizedcargo run # Build and runcargo run -- arg1 arg2 # Pass args to programcargo test # Run testscargo test test_name # Run specific testcargo test -- --nocapture # Show println outputcargo check # Fast syntax/type checkcargo fmt # Format codecargo clippy # Lint codecargo doc --open # Generate and view docscargo update # Update dependenciescargo add crate_name # Add dependencycargo remove crate_name # Remove dependencyCargo.toml
Section titled “Cargo.toml”[package]name = "my_project"version = "0.1.0"edition = "2021"authors = ["Name <email@example.com>"]description = "A brief description"license = "MIT"
[dependencies]serde = "1.0" # Latest 1.xserde_json = "1.0.108" # Exact versiontokio = { version = "1", features = ["full"] }local_crate = { path = "../local" } # Local pathgit_crate = { git = "https://..." } # Git repo
[dev-dependencies]criterion = "0.5" # Only for tests/benches
[build-dependencies]cc = "1.0" # Only for build.rs
[features]default = ["std"]std = []extra = ["dep:optional_crate"]
[[bin]]name = "my_binary"path = "src/bin/main.rs"Crate Structure
Section titled “Crate Structure”Module System
Section titled “Module System”// src/lib.rs or src/main.rs
// Declare modules (looks for file or directory)mod utils; // src/utils.rs or src/utils/mod.rsmod network; // src/network.rs or src/network/mod.rs
// Inline modulemod helpers { pub fn help() {}}
// Re-export for cleaner APIpub use utils::parse;pub use network::Client;File Organization
Section titled “File Organization”src/├── main.rs # Binary entry point├── lib.rs # Library entry point├── utils.rs # mod utils;├── network/ # mod network;│ ├── mod.rs # Module root│ ├── client.rs # network::client│ └── server.rs # network::server└── bin/ └── tool.rs # Additional binaryVisibility
Section titled “Visibility”mod outer { pub mod inner { pub fn public() {} // Visible everywhere pub(crate) fn crate_only() {} // Visible in crate pub(super) fn parent_only() {} // Visible to parent pub(in crate::outer) fn limited() {} // Specific path fn private() {} // Only this module }}
// Struct field visibilitypub struct Config { pub name: String, // Public field pub(crate) debug: bool, // Crate-visible field secret: String, // Private field}Use and Paths
Section titled “Use and Paths”// Absolute path (from crate root)use crate::utils::parse;use crate::network::Client;
// Relative pathuse self::helpers::help; // Current moduleuse super::something; // Parent module
// External crateuse std::collections::HashMap;use serde::{Serialize, Deserialize};
// Glob import (use sparingly)use std::io::*;
// Rename importsuse std::fmt::Result as FmtResult;use std::io::Result as IoResult;
// Nested pathsuse std::{ collections::{HashMap, HashSet}, io::{self, Read, Write},};Prelude Pattern
Section titled “Prelude Pattern”pub use crate::error::Error;pub use crate::config::Config;pub use crate::traits::{Parse, Validate};
// In other modulesuse crate::prelude::*;See Also
Section titled “See Also”- Rust Lesson Plan — 8 lessons from ownership to lifetimes
- Testing — Rust test commands and patterns
- Concurrency Lesson Plan — Threads, channels, async across Go/Python/Rust
- Learning a Language — Phases, techniques, anti-patterns