Cheat Sheet
Need quick Rust syntax lookup? This cheat sheet covers essential Rust patterns, syntax, and commands.
Variables and Bindings
let x = 5; // Immutable binding
let mut y = 10; // Mutable binding
const MAX: u32 = 100; // Constant (must annotate type)
static NAME: &str = "Rust"; // Static variable
let x = 5; // Shadowing (new variable)
let x = x + 1; // Shadow with new value
Data Types
Scalar Types
// Integers
let a: i8 = -128; // 8-bit signed
let b: u32 = 42; // 32-bit unsigned (default: i32)
let c: isize = 100; // Architecture-dependent signed
// Floating-point
let x: f64 = 2.0; // 64-bit float (default)
let y: f32 = 3.0; // 32-bit float
// Boolean
let t: bool = true;
let f = false;
// Character
let c: char = 'z'; // Unicode scalar value (4 bytes)
let emoji = '😎';Compound Types
// Tuple
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup; // Destructuring
let first = tup.0; // Access by index
// Array
let a: [i32; 5] = [1, 2, 3, 4, 5];
let b = [3; 5]; // [3, 3, 3, 3, 3]
let first = a[0];Ownership and Borrowing
Ownership Rules
// 1. Each value has an owner
// 2. Only one owner at a time
// 3. Value dropped when owner out of scope
let s1 = String::from("hello");
let s2 = s1; // s1 moved to s2 (s1 invalid)
// println!("{}", s1); // Error: value moved
let s3 = s2.clone(); // Deep copy (both valid)
References
let s = String::from("hello");
let r1 = &s; // Immutable reference
let r2 = &s; // Multiple immutable refs OK
// Can't modify through immutable reference
let mut s = String::from("hello");
let r = &mut s; // Mutable reference
r.push_str(", world");
// Only ONE mutable reference allowed
Borrowing Rules
// At any time: either
// - One mutable reference (&mut T)
// - Any number of immutable references (&T)
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} {}", r1, r2); // OK: multiple immutable
// r1, r2 no longer used
let r3 = &mut s; // OK: no other refs active
r3.push_str("!");Lifetimes
// Lifetime annotation syntax
&i32 // Reference
&'a i32 // Reference with explicit lifetime
&'a mut i32 // Mutable reference with lifetime
// Function with lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Struct with lifetime
struct ImportantExcerpt<'a> {
part: &'a str,
}
// 'static lifetime (entire program duration)
let s: &'static str = "I live forever";Functions
fn add(x: i32, y: i32) -> i32 {
x + y // Expression (no semicolon)
}
fn nothing() { // Returns () (unit type)
println!("No return");
}
// Early return
fn divide(x: f64, y: f64) -> Option<f64> {
if y == 0.0 {
return None;
}
Some(x / y)
}Control Flow
// if expression
let number = if condition { 5 } else { 6 };
// loop (infinite)
loop {
break; // Exit loop
}
let result = loop {
break 42; // Break with value
};
// while
while condition {
// code
}
// for
for element in array {
println!("{}", element);
}
for i in 0..10 { // Range 0 to 9
println!("{}", i);
}
for i in 0..=10 { // Range 0 to 10 (inclusive)
println!("{}", i);
}
// match (exhaustive)
match value {
1 => println!("One"),
2 | 3 => println!("Two or Three"),
4..=9 => println!("Four through Nine"),
_ => println!("Something else"),
}
// if let
if let Some(x) = optional_value {
println!("{}", x);
}Structs
// Definition
struct User {
username: String,
email: String,
active: bool,
}
// Instantiation
let user = User {
email: String::from("user@example.com"),
username: String::from("user123"),
active: true,
};
// Field access
println!("{}", user.email);
// Tuple struct
struct Color(i32, i32, i32);
let black = Color(0, 0, 0);
// Unit struct
struct AlwaysEqual;
// Methods
impl User {
fn new(email: String, username: String) -> User {
User {
email,
username,
active: true,
}
}
fn deactivate(&mut self) {
self.active = false;
}
}Enums and Pattern Matching
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let msg = Message::Write(String::from("hello"));
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to {}, {}", x, y),
Message::Write(text) => println!("Text: {}", text),
Message::ChangeColor(r, g, b) => println!("RGB: {}, {}, {}", r, g, b),
}
// Option<T>
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
// Result<T, E>
let result: Result<i32, String> = Ok(42);
let error: Result<i32, String> = Err(String::from("error"));Error Handling
// panic! (unrecoverable)
panic!("crash and burn");
// Result<T, E> (recoverable)
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("division by zero"))
} else {
Ok(a / b)
}
}
// ? operator (early return on Err)
fn read_file() -> Result<String, std::io::Error> {
let mut file = File::open("file.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
// unwrap (panics on Err)
let value = some_result.unwrap();
// expect (panics with custom message)
let value = some_result.expect("Failed to read");Collections
// Vec<T>
let mut v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3]; // vec! macro
v.push(4);
let third = &v[2];
let third = v.get(2); // Returns Option<&T>
// String
let mut s = String::new();
let s = String::from("hello");
let s = "hello".to_string();
s.push_str(" world");
s.push('!');
// HashMap<K, V>
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("TeamA"), 50);
let score = scores.get("Blue"); // Option<&V>
scores.entry(String::from("TeamB")).or_insert(50);Traits
// Definition
pub trait Summary {
fn summarize(&self) -> String;
fn summarize_default(&self) -> String {
String::from("(Read more...)")
}
}
// Implementation
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {}", self.headline, self.author)
}
}
// Trait bounds
fn notify<T: Summary>(item: &T) {
println!("{}", item.summarize());
}
fn notify(item: &impl Summary) { // Syntax sugar
println!("{}", item.summarize());
}
// Multiple trait bounds
fn notify<T: Summary + Display>(item: &T) { }
// where clause
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{ }
// Return trait
fn returns_summarizable() -> impl Summary { }Generics
// 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
struct Point<T> {
x: T,
y: T,
}
// Generic impl
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// Specific impl
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}Iterators
let v = vec![1, 2, 3];
// Consuming adapters
let total: i32 = v.iter().sum();
let collected: Vec<i32> = v.iter().collect();
// Iterator adapters
v.iter().map(|x| x + 1)
v.iter().filter(|x| x % 2 == 0)
v.iter().take(5)
v.iter().skip(2)
v.iter().enumerate()
v.iter().zip(other)
// For loop
for item in v.iter() {
println!("{}", item);
}
// Custom iterator
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// Implementation
}
}Closures
// Syntax
let add_one = |x| x + 1;
let add = |x: i32, y: i32| -> i32 { x + y };
// Capturing environment
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x; // Takes ownership
// Fn traits
// Fn: borrows immutably
// FnMut: borrows mutably
// FnOnce: consumes captured variables
Smart Pointers
// Box<T> - heap allocation
let b = Box::new(5);
// Rc<T> - reference counting
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
// Arc<T> - atomic reference counting (thread-safe)
use std::sync::Arc;
let a = Arc::new(5);
// RefCell<T> - interior mutability
use std::cell::RefCell;
let x = RefCell::new(5);
*x.borrow_mut() += 1;Concurrency
use std::thread;
use std::sync::{mpsc, Arc, Mutex};
// Threads
let handle = thread::spawn(|| {
println!("Hello from thread");
});
handle.join().unwrap();
// Channels
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("hello").unwrap();
});
let msg = rx.recv().unwrap();
// Shared state
let counter = Arc::new(Mutex::new(0));
let counter_clone = Arc::clone(&counter);
thread::spawn(move || {
let mut num = counter_clone.lock().unwrap();
*num += 1;
});Async/Await
// async function
async fn fetch_data() -> String {
String::from("data")
}
// await
async fn process() {
let data = fetch_data().await;
}
// Tokio runtime
#[tokio::main]
async fn main() {
let result = fetch_data().await;
}
// Spawn task
tokio::spawn(async {
// async work
});
// Join futures
tokio::join!(fut1, fut2);Modules
// Define module
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
// Use module
use crate::front_of_house::hosting;
hosting::add_to_waitlist();
// External crate
use std::collections::HashMap;
use rand::Rng;
// Renaming
use std::io::Result as IoResult;Testing
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
#[should_panic]
fn it_panics() {
panic!("fail");
}
#[test]
fn returns_result() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("failed"))
}
}
}Cargo Commands
cargo new my_project # Create new binary project
cargo new --lib my_lib # Create new library
cargo init # Initialize in existing directory
cargo build # Build debug
cargo build --release # Build optimized release
cargo run # Build and run
cargo run --release # Run release build
cargo check # Check compilation (faster than build)
cargo test # Run all tests
cargo test test_name # Run specific test
cargo test --lib # Run library tests only
cargo test --doc # Run doc tests only
cargo doc # Build documentation
cargo doc --open # Build and open in browser
cargo add rand # Add dependency (requires cargo-edit)
cargo update # Update dependencies
cargo tree # Show dependency tree
cargo publish # Publish to crates.io
cargo yank --vers 1.0.0 # Yank version from crates.io
cargo new --workspace # Create workspaceTooling Commands
rustup update # Update Rust
rustup default stable # Set default to stable
rustup override set nightly # Use nightly in current directory
rustup component add rustfmt # Add rustfmt
rustup component add clippy # Add clippy
cargo fmt # Format all code
rustfmt src/main.rs # Format specific file
cargo clippy # Run linter
cargo clippy --fix # Apply automatic fixesCommon Patterns
// Error propagation
fn function() -> Result<T, E> {
let x = fallible_operation()?;
Ok(x)
}
// Newtype pattern
struct Wrapper(Vec<String>);
// Type state pattern
struct Locked;
struct Unlocked;
struct Door<State> {
_state: PhantomData<State>,
}
// Builder pattern
Config::new()
.host("localhost")
.port(8080)
.build()
// RAII (Drop)
impl Drop for MyStruct {
fn drop(&mut self) {
// Cleanup
}
}Useful Attributes
#[derive(Debug, Clone, PartialEq)] // Auto-implement traits
#[allow(dead_code)] // Suppress warning
#[cfg(test)] // Compile only in test
#[cfg(target_os = "linux")] // Platform-specific
#[inline] // Suggest inlining
#[must_use] // Warn if result unused
#[deprecated] // Mark as deprecated
Quick Tips
- Ownership: Think “who owns this data?”
- Borrowing: Use
&for read-only,&mutfor write - Lifetimes: Usually inferred, annotate when ambiguous
- Error handling: Use
Resultfor recoverable,panic!for bugs - Performance: Iterators are zero-cost abstractions
- Async: Use for I/O-bound tasks, not CPU-bound
- Testing: Write tests as you code, not after
Keep this cheat sheet handy while coding Rust!
Last updated