# Rust Patterns

> When to activate: general Rust code, ownership, borrowing, lifetimes, enums, pattern matching, traits, structs, idiomatic Rust

- Skill: `mattakushi432/rust-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/rust-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/rust-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/rust-patterns

---


# Rust Patterns

## Ownership and Borrowing

Rust's core: each value has one owner; borrow with `&` (shared) or `&mut` (exclusive).

```rust
fn main() {
    let s1 = String::from("hello"); // s1 owns the string
    let s2 = &s1;                   // s2 borrows s1 (shared)
    println!("{} {}", s1, s2);      // both valid

    let mut v = vec![1, 2, 3];
    let first = &v[0];              // immutable borrow
    // v.push(4);                   // ERROR: cannot mutate while borrowed
    println!("{}", first);
    v.push(4);                      // borrow ended; mutation OK now
}

fn take_ownership(s: String) -> usize { s.len() } // s dropped here
fn borrow(s: &str) -> usize { s.len() }           // caller retains ownership
```

## Enums and Pattern Matching

Rust enums are algebraic data types — use them for state machines and domain modeling.

```rust
#[derive(Debug)]
enum Shape {
    Circle { radius: f64 },
    Rectangle { width: f64, height: f64 },
    Triangle(f64, f64, f64),
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Circle { radius } => std::f64::consts::PI * radius * radius,
            Shape::Rectangle { width, height } => width * height,
            Shape::Triangle(base, height, _) => 0.5 * base * height,
        }
    }
}

// if let for single-variant extraction
fn print_radius(shape: &Shape) {
    if let Shape::Circle { radius } = shape {
        println!("radius: {radius}");
    }
}

// while let for iterating until None
fn drain_queue(queue: &mut Vec<String>) {
    while let Some(item) = queue.pop() {
        println!("processing: {item}");
    }
}
```

## Traits

Define shared behavior. Prefer trait objects (`dyn Trait`) for runtime dispatch, generics for compile-time.

```rust
trait Summarize {
    fn summary(&self) -> String;
    fn title(&self) -> &str { "untitled" } // default implementation
}

struct Article { headline: String, content: String }

impl Summarize for Article {
    fn summary(&self) -> String {
        format!("{}: {}...", self.headline, &self.content[..50.min(self.content.len())])
    }
    fn title(&self) -> &str { &self.headline }
}

// Generic function — monomorphized at compile time (zero cost)
fn print_summary<T: Summarize>(item: &T) {
    println!("{}", item.summary());
}

// Trait object — runtime dispatch
fn print_all(items: &[&dyn Summarize]) {
    for item in items { println!("{}", item.summary()); }
}

// Multiple trait bounds
fn notify(item: &(impl Summarize + std::fmt::Debug)) {
    println!("{:?}: {}", item, item.summary());
}
```

## Struct Patterns

Builder pattern for complex construction; newtype for type safety.

```rust
// Builder pattern
#[derive(Debug, Default)]
struct RequestBuilder {
    url: String,
    timeout_secs: u64,
    headers: Vec<(String, String)>,
}

impl RequestBuilder {
    fn new(url: impl Into<String>) -> Self {
        Self { url: url.into(), timeout_secs: 30, ..Default::default() }
    }
    fn timeout(mut self, secs: u64) -> Self { self.timeout_secs = secs; self }
    fn header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
        self.headers.push((k.into(), v.into())); self
    }
}

// Newtype for type safety
struct UserId(u64);
struct OrderId(u64);
// Cannot accidentally pass UserId where OrderId expected
```

## Option and Result Combinators

Avoid explicit `match` when combinators express intent more clearly.

```rust
// Option combinators
let name: Option<String> = find_user(42).map(|u| u.name.clone());
let display = find_user(42)
    .filter(|u| u.active)
    .map(|u| u.name)
    .unwrap_or_else(|| "anonymous".to_string());

// Result combinators + ? operator
fn get_profile(id: u64) -> Result<Profile, AppError> {
    let user = find_user(id).ok_or(AppError::UserNotFound)?;
    let profile = load_profile(&user).map_err(AppError::Db)?;
    Ok(profile)
}

// Collect Vec<Result<T, E>> into Result<Vec<T>, E>
let ids = vec!["1", "2", "abc"];
let parsed: Result<Vec<u64>, _> = ids.iter()
    .map(|s| s.parse::<u64>())
    .collect();
```

## Iterators

Prefer iterator chains over explicit loops; they compile to the same code.

```rust
let data = vec![1, 2, 3, 4, 5, 6];

let result: Vec<i32> = data.iter()
    .filter(|&&x| x % 2 == 0)
    .map(|&x| x * x)
    .collect();

let sum: i32 = data.iter().filter(|&&x| x > 2).map(|&x| x * x).sum();

// flat_map to flatten nested iterables
let words = vec!["hello world", "foo bar"];
let tokens: Vec<&str> = words.iter()
    .flat_map(|s| s.split_whitespace())
    .collect();

// zip two iterators
let keys = vec!["a", "b", "c"];
let vals = vec![1, 2, 3];
let map: std::collections::HashMap<_, _> = keys.into_iter().zip(vals).collect();
```

## Common Anti-Patterns

- **Cloning to avoid borrow issues** — understand the borrow checker instead of cloning reflexively
- **Panicking with `.unwrap()`** on user-facing paths — use `?` or proper error handling
- **Mutable global state with `static mut`** — use `OnceLock`, `Mutex`, or dependency injection
- **Ignoring `#[must_use]` warnings** — always handle `Result` and `Option` return values
- **Over-using `Box<dyn Trait>`** — prefer generics when the set of types is known at compile time

