Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add jgarrison929/openclaw-skills --skill "rust-expert"
Install specific skill from multi-skill repository
# Description
Use when writing Rust code requiring ownership, borrowing, lifetimes, async/await, error handling, or systems programming. Invoke for memory safety patterns, trait design, Cargo configuration, or performance-critical code.
# SKILL.md
name: rust-expert
description: Use when writing Rust code requiring ownership, borrowing, lifetimes, async/await, error handling, or systems programming. Invoke for memory safety patterns, trait design, Cargo configuration, or performance-critical code.
triggers:
- Rust
- Cargo
- ownership
- borrowing
- lifetime
- trait
- tokio
- async Rust
- serde
- wasm
- no_std
- unsafe
- borrow checker
role: specialist
scope: implementation
output-format: code
Rust Expert
Senior Rust specialist with deep expertise in ownership, concurrency, zero-cost abstractions, and production systems programming.
Role Definition
You are a senior Rust engineer who builds safe, concurrent, and performant systems. You leverage the type system for compile-time guarantees, design clean APIs with proper trait hierarchies, and write code that is both safe and efficient. You know when unsafe is justified and how to document it.
Core Principles
- Leverage the type system β make invalid states unrepresentable
- Zero-cost abstractions β abstractions should compile away
- No unwrap() in production β proper error handling with
Resultand? - Iterator chains over manual loops β functional style is idiomatic
- Clippy is your friend β
#![warn(clippy::all, clippy::pedantic)] - Document unsafe β every
unsafeblock gets a// SAFETY:comment
Project Structure
myproject/
βββ Cargo.toml
βββ Cargo.lock
βββ src/
β βββ main.rs # Binary entry (or lib.rs for library)
β βββ lib.rs # Library root
β βββ error.rs # Error types
β βββ config.rs # Configuration
β βββ models/
β β βββ mod.rs
β β βββ user.rs
β βββ services/
β βββ mod.rs
β βββ user_service.rs
βββ tests/ # Integration tests
β βββ integration_test.rs
βββ benches/ # Benchmarks
β βββ benchmark.rs
βββ examples/
βββ basic_usage.rs
Cargo.toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
[dependencies]
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
anyhow = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
tokio-test = "0.4"
assert_matches = "1.5"
[profile.release]
lto = true
codegen-units = 1
strip = true
[[bench]]
name = "benchmark"
harness = false
[lints.clippy]
all = "warn"
pedantic = "warn"
nursery = "warn"
Ownership, Borrowing, and Lifetimes
// Ownership transfer
fn process_data(data: Vec<u8>) -> Vec<u8> {
// `data` is owned here; caller can no longer use it
data.into_iter().map(|b| b.wrapping_add(1)).collect()
}
// Borrowing β shared reference (&T)
fn analyze(data: &[u8]) -> usize {
data.iter().filter(|&&b| b > 128).count()
}
// Mutable borrowing (&mut T)
fn normalize(data: &mut [f64]) {
let max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
if max > 0.0 {
data.iter_mut().for_each(|v| *v /= max);
}
}
// Lifetimes β when compiler needs help
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
// Struct with lifetime
struct Parser<'input> {
input: &'input str,
position: usize,
}
impl<'input> Parser<'input> {
fn new(input: &'input str) -> Self {
Self { input, position: 0 }
}
fn next_token(&mut self) -> Option<&'input str> {
let remaining = &self.input[self.position..];
let end = remaining.find(char::is_whitespace).unwrap_or(remaining.len());
if end == 0 {
return None;
}
let token = &remaining[..end];
self.position += end + 1;
Some(token)
}
}
// When to use owned vs borrowed
struct Config {
name: String, // Owned β Config controls this data's lifetime
database_url: String,
}
fn greet(name: &str) { // Borrowed β just reading, no need to own
println!("Hello, {name}!");
}
Error Handling
use thiserror::Error;
// Library errors with thiserror
#[derive(Error, Debug)]
pub enum AppError {
#[error("user not found: {id}")]
NotFound { id: String },
#[error("validation failed: {field} β {message}")]
Validation { field: String, message: String },
#[error("database error")]
Database(#[from] sqlx::Error),
#[error("serialization error")]
Serialization(#[from] serde_json::Error),
#[error("internal error: {0}")]
Internal(String),
}
// Result alias for convenience
pub type Result<T> = std::result::Result<T, AppError>;
// Using ? operator for clean error propagation
async fn get_user(pool: &PgPool, id: &str) -> Result<User> {
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_optional(pool)
.await? // Database error auto-converts via From
.ok_or_else(|| AppError::NotFound { id: id.to_string() })?;
Ok(user)
}
// Application-level with anyhow for binaries
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let contents = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read config from {path}"))?;
let config: Config = toml::from_str(&contents)
.context("Failed to parse config TOML")?;
Ok(config)
}
Traits and Generics
// Define traits with associated types
trait Repository {
type Entity;
type Error;
async fn find_by_id(&self, id: &str) -> Result<Option<Self::Entity>, Self::Error>;
async fn save(&self, entity: &Self::Entity) -> Result<(), Self::Error>;
async fn delete(&self, id: &str) -> Result<bool, Self::Error>;
}
// Implement for concrete type
struct PgUserRepo {
pool: PgPool,
}
impl Repository for PgUserRepo {
type Entity = User;
type Error = AppError;
async fn find_by_id(&self, id: &str) -> Result<Option<User>> {
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_optional(&self.pool)
.await?;
Ok(user)
}
// ...
}
// Trait bounds for generic functions
fn print_all<T: std::fmt::Display>(items: &[T]) {
for item in items {
println!("{item}");
}
}
// Where clause for complex bounds
fn process<T>(item: T) -> String
where
T: std::fmt::Debug + Clone + Send + 'static,
{
format!("{item:?}")
}
// Newtype pattern for type safety
struct UserId(String);
struct OrderId(String);
// Can't accidentally pass OrderId where UserId is expected
fn get_user(id: &UserId) -> Result<User> { ... }
// Builder pattern
#[derive(Default)]
struct RequestBuilder {
url: Option<String>,
method: Method,
headers: Vec<(String, String)>,
timeout: Option<Duration>,
}
impl RequestBuilder {
fn new() -> Self { Self::default() }
fn url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((key.into(), value.into()));
self
}
fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
fn build(self) -> Result<Request> {
let url = self.url.ok_or_else(|| AppError::Validation {
field: "url".into(),
message: "URL is required".into(),
})?;
Ok(Request { url, method: self.method, headers: self.headers, timeout: self.timeout })
}
}
Async / Tokio
use tokio::sync::{mpsc, Semaphore};
use std::sync::Arc;
// Async function
async fn fetch_data(url: &str) -> Result<String> {
let response = reqwest::get(url).await?.text().await?;
Ok(response)
}
// Concurrent tasks with join
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
let handles: Vec<_> = urls
.into_iter()
.map(|url| tokio::spawn(async move { fetch_data(&url).await }))
.collect();
let mut results = Vec::new();
for handle in handles {
match handle.await {
Ok(result) => results.push(result),
Err(e) => results.push(Err(AppError::Internal(e.to_string()))),
}
}
results
}
// Rate-limited concurrent processing
async fn process_with_limit(items: Vec<Item>, max_concurrent: usize) -> Vec<Result<Output>> {
let semaphore = Arc::new(Semaphore::new(max_concurrent));
let mut handles = Vec::new();
for item in items {
let permit = semaphore.clone().acquire_owned().await.unwrap();
handles.push(tokio::spawn(async move {
let result = process_item(item).await;
drop(permit);
result
}));
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.await.unwrap_or_else(|e| Err(AppError::Internal(e.to_string()))));
}
results
}
// Channel-based actor pattern
struct Actor {
receiver: mpsc::Receiver<Message>,
state: HashMap<String, String>,
}
impl Actor {
fn new(receiver: mpsc::Receiver<Message>) -> Self {
Self { receiver, state: HashMap::new() }
}
async fn run(&mut self) {
while let Some(msg) = self.receiver.recv().await {
match msg {
Message::Get { key, respond_to } => {
let value = self.state.get(&key).cloned();
let _ = respond_to.send(value);
}
Message::Set { key, value } => {
self.state.insert(key, value);
}
}
}
}
}
// Graceful shutdown
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(false);
let server = tokio::spawn(async move {
// Run server...
shutdown_rx.changed().await.ok();
// Cleanup...
});
tokio::signal::ctrl_c().await?;
shutdown_tx.send(true)?;
server.await?;
Ok(())
}
Iterators and Functional Patterns
// Iterator chains β preferred over manual loops
let active_users: Vec<&User> = users
.iter()
.filter(|u| u.is_active)
.filter(|u| u.age >= 18)
.collect();
// Map, filter, fold
let total_revenue: f64 = orders
.iter()
.filter(|o| o.status == OrderStatus::Completed)
.map(|o| o.total)
.sum();
// Flat map for nested structures
let all_items: Vec<&Item> = orders
.iter()
.flat_map(|o| o.items.iter())
.collect();
// Custom iterator
struct Fibonacci {
a: u64,
b: u64,
}
impl Fibonacci {
fn new() -> Self { Self { a: 0, b: 1 } }
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let result = self.a;
(self.a, self.b) = (self.b, self.a.checked_add(self.b)?);
Some(result)
}
}
// Using: first 20 fibonacci numbers
let fibs: Vec<u64> = Fibonacci::new().take(20).collect();
// Chaining with Result
fn parse_and_sum(inputs: &[&str]) -> Result<i64, std::num::ParseIntError> {
inputs.iter().map(|s| s.parse::<i64>()).sum()
}
Enums and Pattern Matching
// State machine with enums
#[derive(Debug)]
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { session_id: String },
Error { message: String, retry_after: Duration },
}
impl ConnectionState {
fn transition(&self, event: Event) -> Self {
match (self, event) {
(Self::Disconnected, Event::Connect) => {
Self::Connecting { attempt: 1 }
}
(Self::Connecting { attempt }, Event::Success(session_id)) => {
Self::Connected { session_id }
}
(Self::Connecting { attempt }, Event::Failure(msg)) if *attempt < 3 => {
Self::Connecting { attempt: attempt + 1 }
}
(Self::Connecting { .. }, Event::Failure(msg)) => {
Self::Error { message: msg, retry_after: Duration::from_secs(30) }
}
(Self::Connected { .. }, Event::Disconnect) => Self::Disconnected,
_ => panic!("Invalid state transition"),
}
}
}
// Exhaustive matching ensures all cases handled
fn format_status(state: &ConnectionState) -> String {
match state {
ConnectionState::Disconnected => "Offline".into(),
ConnectionState::Connecting { attempt } => format!("Connecting (attempt {attempt})"),
ConnectionState::Connected { session_id } => format!("Online ({session_id})"),
ConnectionState::Error { message, .. } => format!("Error: {message}"),
}
}
Testing
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_input() {
let result = parse_config("key=value");
assert!(result.is_ok());
assert_eq!(result.unwrap().key, "key");
}
#[test]
fn test_parse_invalid_input() {
let result = parse_config("invalid");
assert!(result.is_err());
assert_matches!(result, Err(AppError::Validation { .. }));
}
#[test]
#[should_panic(expected = "empty input")]
fn test_panics_on_empty() {
parse_config("");
}
// Async tests
#[tokio::test]
async fn test_fetch_user() {
let pool = setup_test_db().await;
let repo = PgUserRepo::new(pool);
let user = repo.find_by_id("test-1").await.unwrap();
assert!(user.is_some());
assert_eq!(user.unwrap().name, "Alice");
}
// Property-based testing with proptest
use proptest::prelude::*;
proptest! {
#[test]
fn test_roundtrip_serialization(name in "[a-z]{1,20}") {
let user = User { name: name.clone(), ..Default::default() };
let json = serde_json::to_string(&user).unwrap();
let parsed: User = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name, name);
}
}
}
// Integration tests (tests/ directory)
// tests/integration_test.rs
use myproject::UserService;
#[tokio::test]
async fn test_user_workflow() {
let app = TestApp::spawn().await;
let user = app.create_user("Alice", "[email protected]").await;
assert_eq!(user.name, "Alice");
let found = app.get_user(&user.id).await;
assert_eq!(found.email, "[email protected]");
}
// Benchmarks (benches/ directory)
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn bench_parse(c: &mut Criterion) {
let input = generate_test_input(1000);
c.bench_function("parse_1000_items", |b| {
b.iter(|| parse_items(black_box(&input)))
});
}
criterion_group!(benches, bench_parse);
criterion_main!(benches);
Common Anti-Patterns
// β BAD: unwrap() in production
let user = get_user(id).unwrap();
// β
GOOD: Proper error handling
let user = get_user(id)?;
// β BAD: Clone to satisfy borrow checker without understanding why
let data = expensive_data.clone();
process(data);
// β
GOOD: Use references or restructure
process(&expensive_data);
// β BAD: String for all text
fn greet(name: String) { ... }
// β
GOOD: Accept &str, return String
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
// β BAD: Box<dyn Error> everywhere (erases type info)
fn do_thing() -> Result<(), Box<dyn std::error::Error>> { ... }
// β
GOOD: Concrete error types with thiserror
fn do_thing() -> Result<(), AppError> { ... }
// β BAD: Mutex<Vec<T>> when you need concurrent reads
let data = Arc::new(Mutex::new(vec![...]));
// β
GOOD: RwLock for read-heavy workloads
let data = Arc::new(RwLock::new(vec![...]));
Useful Cargo Commands
# Format, lint, test cycle
cargo fmt
cargo clippy -- -W clippy::pedantic
cargo test
cargo test -- --nocapture # Show println output
# Run benchmarks
cargo bench
# Check without building (faster feedback)
cargo check
# Generate docs
cargo doc --open
# Expand macros (requires cargo-expand)
cargo expand
# Audit dependencies for vulnerabilities
cargo audit
# Minimal dependency versions check
cargo +nightly -Z minimal-versions check
Adapted from buildwithclaude by Dave Poon (MIT)
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.