From claude-mods
Provides references and patterns for Rust ownership, borrowing, lifetimes, async with tokio, error handling via anyhow/thiserror, and crates like axum, sqlx, reqwest.
npx claudepluginhub 0xdarkmatter/claude-modsThis skill is limited to using the following tools:
Comprehensive Rust skill covering ownership, async, error handling, and the production ecosystem.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Comprehensive Rust skill covering ownership, async, error handling, and the production ecosystem.
Who owns the value?
│
├─ Need to transfer ownership
│ └─ Move: let s2 = s1; (s1 is invalid after this)
│
├─ Need to read without owning
│ └─ Shared borrow: &T (multiple allowed, no mutation)
│
├─ Need to mutate without owning
│ └─ Exclusive borrow: &mut T (only one, no other borrows)
│
├─ Need to share ownership across threads
│ └─ Arc<T> (atomic reference counting)
│ └─ Need mutation too? Arc<Mutex<T>>
│
├─ Need to share ownership single-threaded
│ └─ Rc<T> (reference counting, not Send)
│ └─ Need mutation too? Rc<RefCell<T>>
│
└─ Need to avoid cloning large data
└─ Cow<'a, T> (clone-on-write, borrows when possible)
&mut T or any number of &TWhat kind of error?
│
├─ Operation might not have a value (no error info needed)
│ └─ Option<T>: Some(value) or None
│
├─ Library code (callers need to match on error variants)
│ └─ thiserror: #[derive(Error)] enum with variants
│ └─ Each variant can wrap source errors with #[from]
│
├─ Application code (just need context, not matching)
│ └─ anyhow: anyhow::Result<T>, .context("msg")
│
├─ Converting between error types
│ └─ impl From<SourceError> for MyError
│ └─ Or use #[from] with thiserror
│
└─ Truly unrecoverable (violating invariants)
└─ panic!() or unwrap() - avoid in library code
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("not found: {entity} with id {id}")]
NotFound { entity: &'static str, id: i64 },
#[error("validation failed: {0}")]
Validation(String),
}
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.context("failed to read config file")?;
let config: Config = toml::from_str(&content)
.context("failed to parse config")?;
Ok(config)
}
// ? on Result: returns Err early, unwraps Ok
let file = File::open(path)?;
// ? on Option: returns None early, unwraps Some
let first = items.first()?;
// Chain with map_err for context
let port: u16 = env::var("PORT")
.map_err(|_| AppError::Config("PORT not set"))?
.parse()
.map_err(|_| AppError::Config("PORT not a number"))?;
Deep dive: Load ./references/error-handling.md for Result/Option combinators, error conversion patterns, panic/recover.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] // Value types
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] // API types
#[derive(Debug, thiserror::Error)] // Error types
Trait Objects (dyn Trait) | Generics (T: Trait) | |
|---|---|---|
| Dispatch | Dynamic (vtable) | Static (monomorphized) |
| Binary size | Smaller | Larger (per-type copies) |
| Performance | Slight overhead | Zero-cost |
| Heterogeneous collections | Yes | No |
| Use when | Runtime polymorphism, plugin systems | Performance-critical, known types |
// Generics (preferred when types known at compile time)
fn process<T: Display>(item: T) { println!("{item}"); }
// Trait objects (when you need heterogeneous collections)
fn process_all(items: &[Box<dyn Display>]) {
for item in items { println!("{item}"); }
}
| Trait | Purpose | Auto-derive? |
|---|---|---|
Debug | Debug formatting | Yes |
Clone | Explicit copy | Yes |
Copy | Implicit copy (small, stack-only) | Yes |
Display | User-facing formatting | No (impl manually) |
From/Into | Type conversion | No (impl From, get Into free) |
Send | Safe to send between threads | Auto |
Sync | Safe to share references between threads | Auto |
Deref | Smart pointer dereference | No |
Iterator | Iteration protocol | No |
Default | Default value | Yes |
Deep dive: Load ./references/traits-generics.md for associated types, supertraits, sealed traits, extension traits.
Do you need async?
│
├─ I/O-heavy (network, files, databases)
│ └─ Yes. Use tokio.
│
├─ CPU-heavy computation
│ └─ No. Use rayon for data parallelism.
│ └─ Or tokio::task::spawn_blocking for mixing with async
│
├─ Simple scripts or CLI tools
│ └─ Probably not. Blocking I/O is fine.
│
└─ Yes, I need async:
│
├─ Runtime: tokio (dominant), or async-std
├─ HTTP client: reqwest
├─ HTTP server: axum (tower-based) or actix-web
├─ Database: sqlx (compile-time checked)
└─ Structured logging: tracing
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Spawn concurrent tasks
let (a, b) = tokio::join!(
fetch_users(),
fetch_orders(),
);
// Select first to complete
tokio::select! {
result = long_operation() => handle(result),
_ = tokio::time::sleep(Duration::from_secs(5)) => {
eprintln!("timeout");
}
}
Ok(())
}
| Channel | Use Case | Import |
|---|---|---|
mpsc | Multiple producers, single consumer | tokio::sync::mpsc |
oneshot | Single value, single use | tokio::sync::oneshot |
broadcast | Multiple consumers, all get every message | tokio::sync::broadcast |
watch | Single value, latest-only (config reload) | tokio::sync::watch |
Deep dive: Load ./references/async-tokio.md for spawn patterns, graceful shutdown, Mutex choice, async traits, streams.
# Create project
cargo new my-project # binary
cargo new my-lib --lib # library
# Build and run
cargo build # debug
cargo build --release # optimized
cargo run -- args # build + run
cargo run --example name # run example
# Test
cargo test # all tests
cargo test test_name # specific test
cargo test -- --nocapture # show println output
# Dependencies
cargo add serde --features derive # add dep
cargo add tokio -F full # shorthand
cargo update # update lock file
# Check without building
cargo check # fast type checking
cargo clippy # lints
cargo fmt # format
# Workspace
cargo test --workspace # test all crates
cargo build -p my-crate # build specific crate
[features]
default = ["json"]
json = ["dep:serde_json"]
full = ["json", "yaml", "toml"]
[dependencies]
serde_json = { version = "1", optional = true }
| Gotcha | Why | Fix |
|---|---|---|
String vs &str | Owned vs borrowed, function signatures | Accept &str in params, return String |
| Borrow checker fight | Borrowing self while mutating | Split struct, use indices, clone (if cheap) |
| Lifetime elision confusion | Hidden lifetimes in function signatures | Write them out explicitly to understand, then elide |
impl Trait in return | Different branches must return same type | Use Box<dyn Trait> for heterogeneous returns |
tokio::Mutex vs std::Mutex | std::Mutex can't be held across .await | Use tokio::Mutex across await points |
| Orphan rule | Can't impl foreign trait for foreign type | Newtype pattern: struct Wrapper(ForeignType) |
Pin confusion | Required for self-referential async futures | Use Box::pin(), don't fight it |
Send bounds on async | Spawned futures must be Send | Avoid Rc, RefCell in async; use Arc, Mutex |
.unwrap() in production | Panics on None/Err | Use ?, .unwrap_or(), .expect("reason") |
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
user_id: i64,
display_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[serde(default)]
is_active: bool,
#[serde(rename = "type")]
user_type: UserType,
#[serde(with = "chrono::serde::ts_seconds")]
created_at: DateTime<Utc>,
}
// Serialize
let json = serde_json::to_string(&user)?;
let yaml = serde_yaml::to_string(&user)?;
// Deserialize
let user: User = serde_json::from_str(&json)?;
Deep dive: Load ./references/ecosystem.md for serde advanced usage, clap, reqwest, sqlx, axum, tracing, rayon.
Load these for deep-dive topics. Each is self-contained.
| Reference | When to Load |
|---|---|
./references/ownership-lifetimes.md | Borrowing rules, lifetime annotations, elision, interior mutability, common borrow checker patterns |
./references/traits-generics.md | Trait design, associated types, supertraits, generics, constraints, sealed/extension traits |
./references/error-handling.md | Result/Option combinators, thiserror/anyhow deep dive, error conversion, panic/recover |
./references/async-tokio.md | tokio runtime, spawn, channels, select, streams, graceful shutdown, async traits, Mutex choice |
./references/ecosystem.md | serde advanced, clap, reqwest, sqlx, axum, tracing, rayon, itertools, Cow |
./references/testing.md | Unit/integration/doc tests, async tests, mockall, proptest, criterion benchmarks |
docker-ops - Multi-stage builds for Rust (scratch/distroless, cargo-chef for layer caching)ci-cd-ops - Rust CI pipelines, cargo caching, cross-compilationtesting-ops - Cross-language testing strategies