| name | rust-ops |
| description | Rust development patterns, ownership, async, error handling, and ecosystem. Use for: rust, cargo, ownership, borrow checker, lifetime, tokio, serde, trait, Result, Option, async rust, crate, derive, impl, enum, pattern matching, Arc, Mutex, Send, Sync, thiserror, anyhow, clap, axum, sqlx, reqwest, rayon, tracing. Use when this capability is needed. |
Rust Operations
Comprehensive Rust skill covering ownership, async, error handling, and the production ecosystem.
Ownership Quick Reference
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)
The Borrow Rules
- At any time, you can have either one
&mut T or any number of &T
- References must always be valid (no dangling)
- These rules are enforced at compile time (zero runtime cost)
Error Handling Decision Tree
What 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
thiserror (Library Errors)
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),
}
anyhow (Application Errors)
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)
}
The ? Operator
let file = File::open(path)?;
let first = items.first()?;
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.
Trait Design Quick Reference
Common Derives
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(Debug, thiserror::Error)]
Trait Objects vs Generics
| 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 |
fn process<T: Display>(item: T) { println!("{item}"); }
fn process_all(items: &[Box<dyn Display>]) {
for item in items { println!("{item}"); }
}
Key Traits to Know
| 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.
Async Decision Tree
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 Quick Start
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (a, b) = tokio::join!(
fetch_users(),
fetch_orders(),
);
tokio::select! {
result = long_operation() => handle(result),
_ = tokio::time::sleep(Duration::from_secs(5)) => {
eprintln!("timeout");
}
}
Ok(())
}
Channel Types
| 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.
Cargo Quick Reference
cargo new my-project
cargo new my-lib --lib
cargo build
cargo build --release
cargo run -- args
cargo run --example name
cargo test
cargo test test_name
cargo test -- --nocapture
cargo add serde --features derive
cargo add tokio -F full
cargo update
cargo check
cargo clippy
cargo fmt
cargo test --workspace
cargo build -p my-crate
Feature Flags
[features]
default = ["json"]
json = ["dep:serde_json"]
full = ["json", "yaml", "toml"]
[dependencies]
serde_json = { version = "1", optional = true }
Release Profile Tuning
[profile.release]
lto = true
codegen-units = 1
Common Gotchas
| 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") |
serde Quick Reference
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>,
}
let json = serde_json::to_string(&user)?;
let yaml = serde_yaml::to_string(&user)?;
let user: User = serde_json::from_str(&json)?;
Deep dive: Load ./references/ecosystem.md for serde advanced usage, clap, reqwest, sqlx, axum, tracing, rayon.
Reference Files
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 |
See Also
docker-ops - Multi-stage builds for Rust (scratch/distroless, cargo-chef for layer caching)
ci-cd-ops - Rust CI pipelines, cargo caching, cross-compilation
testing-ops - Cross-language testing strategies
Source: 0xDarkMatter/claude-mods โ distributed by TomeVault.