Skip to content
Back to skills

Rust Patterns

ASecurity

Rust best practices, patterns, and idioms. Covers ownership, error handling with Result/Option, traits, lifetimes, and async Rust. Use when writing Rust code or reviewing Rust patterns.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 14, 2026
ai-agentsrustgosqltestingdatabasesecurity

Works with

  • claude code
  • cli

Security analysis

A100/100

Scanned October 14, 2026

npx -y skills add peopleforrester/claude-dotfiles --skill rust-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rust Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rust Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/peopleforrester-rust-patterns/badge)](https://www.skillsdirectory.com/skills/peopleforrester-rust-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rust-patterns
description: |
  Rust best practices, patterns, and idioms. Covers ownership, error handling
  with Result/Option, traits, lifetimes, and async Rust. Use when writing
  Rust code or reviewing Rust patterns.
license: MIT
compatibility: Claude Code 2.1+
metadata:
  author: peopleforrester
  version: "1.0.0"
  tags:
    - patterns
    - rust
    - language
---

# Rust Patterns

Modern Rust patterns and best practices.

## Error Handling

### Custom Error Types with thiserror
```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Not found: {0}")]
    NotFound(String),

    #[error("Unauthorized: {0}")]
    Unauthorized(String),

    #[error("Database error")]
    Database(#[from] sqlx::Error),

    #[error("IO error")]
    Io(#[from] std::io::Error),
}
```

### Propagation with ?
```rust
fn read_config(path: &str) -> Result<Config, AppError> {
    let content = std::fs::read_to_string(path)?;  // Auto-converts io::Error
    let config: Config = serde_json::from_str(&content)
        .map_err(|e| AppError::NotFound(format!("Invalid config: {e}")))?;
    Ok(config)
}
```

### Option Combinators
```rust
fn find_user(id: &str) -> Option<User> {
    users.iter()
        .find(|u| u.id == id)
        .filter(|u| u.is_active)
        .map(|u| u.clone())
}

// Chain with unwrap_or, unwrap_or_else, unwrap_or_default
let name = user.name.as_deref().unwrap_or("Anonymous");
```

## Ownership and Borrowing

### Prefer Borrowing
```rust
// Good: Borrows data, doesn't take ownership
fn process(data: &[u8]) -> Result<Output, Error> { /* ... */ }

// Good: Mutable borrow when needed
fn update(config: &mut Config) { /* ... */ }

// Take ownership only when necessary (storing, sending to thread)
fn spawn_worker(data: Vec<u8>) { /* ... */ }
```

### Builder Pattern
```rust
pub struct ServerBuilder {
    addr: String,
    port: u16,
    max_connections: Option<usize>,
}

impl ServerBuilder {
    pub fn new(addr: impl Into<String>) -> Self {
        Self {
            addr: addr.into(),
            port: 8080,
            max_connections: None,
        }
    }

    pub fn port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    pub fn max_connections(mut self, max: usize) -> Self {
        self.max_connections = Some(max);
        self
    }

    pub fn build(self) -> Server {
        Server { /* ... */ }
    }
}

// Usage: Server::builder("localhost").port(3000).build()
```

## Traits

### Define Behavior, Not Data
```rust
pub trait Repository {
    type Error;

    fn find_by_id(&self, id: &str) -> Result<Option<User>, Self::Error>;
    fn save(&self, user: &User) -> Result<(), Self::Error>;
    fn delete(&self, id: &str) -> Result<bool, Self::Error>;
}
```

### Blanket Implementations
```rust
// Implement for all types that satisfy bounds
impl<T: Display + Debug> Loggable for T {
    fn log(&self) {
        println!("[LOG] {self}");
    }
}
```

## Async Rust

### Tokio Patterns
```rust
use tokio::task::JoinSet;

async fn fetch_all(urls: Vec<String>) -> Vec<Result<Response, Error>> {
    let mut set = JoinSet::new();

    for url in urls {
        set.spawn(async move {
            reqwest::get(&url).await?.text().await
        });
    }

    let mut results = Vec::new();
    while let Some(result) = set.join_next().await {
        results.push(result.unwrap());
    }
    results
}
```

### Graceful Shutdown
```rust
use tokio::signal;

async fn run_server() -> Result<(), Error> {
    let server = start_server().await?;

    signal::ctrl_c().await?;
    println!("Shutting down...");

    server.shutdown().await;
    Ok(())
}
```

## Tooling

| Tool | Purpose | Command |
|------|---------|---------|
| cargo check | Type checking | `cargo check` |
| cargo clippy | Linting | `cargo clippy -- -D warnings` |
| cargo fmt | Formatting | `cargo fmt -- --check` |
| cargo test | Testing | `cargo test` |
| cargo audit | Security | `cargo audit` |

## Checklist

- [ ] `clippy` passes with no warnings
- [ ] Error types defined with `thiserror`
- [ ] `?` operator for error propagation
- [ ] Prefer borrowing over ownership transfer
- [ ] `Clone` only when necessary
- [ ] `Send + Sync` bounds for async types
- [ ] No `unwrap()` in library code (use `?` or `expect`)
- [ ] Builder pattern for types with many options

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.