Rust Cheatsheet

Enums and Pattern Matching

Use this Rust reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Defining Enums

// Simple enum (C-style)
enum Direction {
    North,
    South,
    East,
    West,
}

// Enum with associated data — each variant can hold different types
enum Message {
    Quit,                        // no data
    Move { x: i32, y: i32 },    // named fields (struct-like)
    Write(String),               // single value
    ChangeColor(i32, i32, i32),  // tuple-like
}

// Enum with explicit discriminants
enum Status {
    Active = 1,
    Inactive = 2,
    Pending = 3,
}

Instantiating Enum Variants

let dir = Direction::North;
let msg1 = Message::Quit;
let msg2 = Message::Move { x: 10, y: 20 };
let msg3 = Message::Write(String::from("hello"));
let msg4 = Message::ChangeColor(255, 0, 128);
let code = Status::Active as i32;  // 1  — cast C-style enum to int

Methods on Enums

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("Quit"),
            Message::Move { x, y } => println!("Move to ({}, {})", x, y),
            Message::Write(s) => println!("Write: {}", s),
            Message::ChangeColor(r, g, b) => println!("Color: {}, {}, {}", r, g, b),
        }
    }

    fn is_quit(&self) -> bool {
        matches!(self, Message::Quit)
    }
}

match on Enums

let msg = Message::Move { x: 5, y: 10 };

match msg {
    Message::Quit => {
        println!("Quit");
    }
    Message::Move { x, y } => {
        println!("Move to ({}, {})", x, y);
    }
    Message::Write(text) => {
        println!("Write: {}", text);
    }
    Message::ChangeColor(r, g, b) => {
        println!("Color: {} {} {}", r, g, b);
    }
}

// Binding with @
let n = 15u32;
match n {
    0 => println!("zero"),
    small @ 1..=9 => println!("small: {}", small),
    large @ 10..=99 => println!("large: {}", large),
    _ => println!("huge"),
}

// Ignoring fields
match msg {
    Message::Move { x, .. } => println!("x = {}", x),
    _ => {}
}

Option\<T\>

The standard "nullable" type — no null pointers in Rust.

enum Option<T> {
    Some(T),
    None,
}
let some_val: Option<i32> = Some(42);
let no_val: Option<i32> = None;

// Unpacking methods
some_val.unwrap()                   // 42, panics if None
some_val.expect("should have value")
some_val.unwrap_or(0)               // 42 or 0
some_val.unwrap_or_else(|| 0)
some_val.unwrap_or_default()        // Default::default()

// Query
some_val.is_some()    // true
some_val.is_none()    // false

// Transform
some_val.map(|x| x * 2)            // Some(84)
some_val.map_or(0, |x| x * 2)      // 84 (or 0 for None)
some_val.and_then(|x| Some(x + 1)) // Some(43) — flatMap
some_val.or(Some(99))               // self if Some, other if None
some_val.or_else(|| Some(99))
some_val.filter(|&x| x > 50)       // None (42 < 50)
some_val.flatten()                  // Option<Option<T>> → Option<T>
some_val.zip(Some("hi"))            // Some((42, "hi"))

// Mutation
let mut opt = Some(42);
opt.take()          // returns Some(42), opt is now None
opt.replace(99)     // returns None (old value), opt is Some(99)
opt.get_or_insert(0)  // returns &mut 99

// Convert
some_val.ok_or("error")            // Result<i32, &str>
some_val.ok_or_else(|| "error".to_string())
some_val.as_ref()                  // Option<&i32>
some_val.as_mut()                  // Option<&mut i32>

// Pattern matching
if let Some(x) = some_val {
    println!("{}", x);
}
let Some(x) = some_val else { return; };  // let-else

// ? in functions returning Option
fn find_double(v: &[i32], target: i32) -> Option<i32> {
    let idx = v.iter().position(|&x| x == target)?;  // return None if not found
    Some(v[idx] * 2)
}

Result\<T, E\>

enum Result<T, E> {
    Ok(T),
    Err(E),
}
let ok: Result<i32, String> = Ok(42);
let err: Result<i32, String> = Err("something went wrong".to_string());

// Unwrapping
ok.unwrap()                 // 42, panics if Err
ok.expect("should be ok")
ok.unwrap_or(0)
ok.unwrap_or_else(|e| { eprintln!("{}", e); 0 })
ok.unwrap_or_default()

// Query
ok.is_ok()    // true
ok.is_err()   // false

// Transform
ok.map(|x| x * 2)                        // Ok(84)
ok.map_err(|e| format!("Error: {}", e))  // transform error
ok.and_then(|x| Ok(x + 1))              // Ok(43) — flatMap on success
ok.or(Err("other"))                       // self if Ok, other if Err
ok.or_else(|e| Err(format!("!{}", e)))

// Extract
ok.ok()   // Option<T> — discards error
ok.err()  // Option<E> — discards success

// Iteration
ok.iter()     // iterates 0 or 1 elements
for val in &ok { println!("{}", val); }

The ? Operator (Error Propagation)

use std::num::ParseIntError;

fn parse_and_double(s: &str) -> Result<i32, ParseIntError> {
    let n: i32 = s.parse()?;  // return Err if parse fails
    Ok(n * 2)
}

// Equivalent to:
fn parse_and_double_explicit(s: &str) -> Result<i32, ParseIntError> {
    let n: i32 = match s.parse() {
        Ok(v) => v,
        Err(e) => return Err(e),
    };
    Ok(n * 2)
}

// Works in functions returning Option too:
fn first_double(v: &[i32]) -> Option<i32> {
    let first = v.first()?;   // return None if empty
    Some(first * 2)
}

// ? converts error type using From trait
use std::io;
fn read_file(path: &str) -> Result<String, io::Error> {
    let content = std::fs::read_to_string(path)?;  // io::Error auto-converted
    Ok(content)
}

Comprehensive Pattern Matching

Binding Patterns

let x = 5;
match x {
    n @ 1..=10 => println!("n = {}", n),   // bind matched value
    n => println!("other: {}", n),
}

Guard Conditions

let pair = (2, -2);
match pair {
    (x, y) if x == y => println!("equal"),
    (x, y) if x + y == 0 => println!("zero sum"),  // matches (2, -2)
    _ => println!("other"),
}

OR Patterns

let x = 'e';
match x {
    'a' | 'e' | 'i' | 'o' | 'u' => println!("vowel"),
    'a'..='z' => println!("consonant"),
    _ => println!("other"),
}

Nested and Destructuring Patterns

struct Point { x: i32, y: i32 }
enum Shape { Circle(Point, f64), Rectangle(Point, Point) }

let shape = Shape::Circle(Point { x: 0, y: 0 }, 5.0);
match shape {
    Shape::Circle(Point { x, y }, r) => {
        println!("Circle at ({},{}) r={}", x, y, r);
    }
    Shape::Rectangle(Point { x: x1, y: y1 }, Point { x: x2, y: y2 }) => {
        println!("Rect ({},{}) to ({},{})", x1, y1, x2, y2);
    }
}

// Ignore fields with ..
let Point { x, .. } = Point { x: 3, y: 5 };

// Ignore specific fields with _
match (1, 2, 3) {
    (first, _, third) => println!("{} {}", first, third),
}

// Ignore entire value with _name (no unused warning)
let _unused = 42;

Matching References

let reference = &4;
match reference {
    &val => println!("Got a value via destructuring: {}", val),
}

// Match guard automatically references
let v = vec![1, 2, 3];
match v.iter().find(|&&x| x > 1) {
    Some(&n) => println!("Found: {}", n),
    None => println!("Not found"),
}

Slice Patterns

let v = vec![1, 2, 3, 4, 5];
match v.as_slice() {
    [] => println!("empty"),
    [x] => println!("one: {}", x),
    [x, y] => println!("two: {} {}", x, y),
    [first, .., last] => println!("first={} last={}", first, last),
    [head, tail @ ..] => println!("head={} tail_len={}", head, tail.len()),
}

Enum with Generic Data

#[derive(Debug)]
enum Tree<T> {
    Leaf(T),
    Node { value: T, left: Box<Tree<T>>, right: Box<Tree<T>> },
}

let tree = Tree::Node {
    value: 1,
    left: Box::new(Tree::Leaf(2)),
    right: Box::new(Tree::Leaf(3)),
};

Deriving Traits for Enums

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Color { Red, Green, Blue }

use std::collections::HashMap;
let mut counts: HashMap<Color, i32> = HashMap::new();
counts.insert(Color::Red, 3);

println!("{:?}", Color::Green);  // Green
let c = Color::Red.clone();
assert_eq!(c, Color::Red);

Non-Exhaustive Enums

// In a library: mark enum as non-exhaustive to allow adding variants later
// without breaking downstream code
#[non_exhaustive]
pub enum Error {
    IoError,
    ParseError,
}

// Downstream users MUST include a _ wildcard arm:
match err {
    Error::IoError => {},
    Error::ParseError => {},
    _ => {},  // required even though all known variants are covered
}