Agent skill

rust-best-practices

Rust development best practices, patterns, and conventions. Use when writing Rust code, reviewing .rs files, discussing ownership, lifetimes, borrowing, or Cargo configuration. Triggers on mentions of Rust, Cargo, ownership, borrowing, lifetimes, traits, async Rust, tokio.

Stars 0
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/eous/dotclaude/tree/main/skills/rust-best-practices

SKILL.md

Rust Best Practices

Ownership and Borrowing

Prefer Borrowing Over Ownership

rust
// Bad - takes ownership unnecessarily
fn print_name(name: String) {
    println!("{}", name);
}

// Good - borrows immutably
fn print_name(name: &str) {
    println!("{}", name);
}

Use References Appropriately

rust
// Immutable borrow for reading
fn calculate_length(s: &String) -> usize {
    s.len()
}

// Mutable borrow for modification
fn push_char(s: &mut String, c: char) {
    s.push(c);
}

Error Handling

Use Result and Option

rust
fn find_user(id: u32) -> Option<User> {
    users.get(&id).cloned()
}

fn parse_config(path: &str) -> Result<Config, ConfigError> {
    let content = fs::read_to_string(path)?;
    toml::from_str(&content).map_err(ConfigError::Parse)
}

Custom Error Types

rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("not found: {0}")]
    NotFound(String),

    #[error("validation error: {field}")]
    Validation { field: String },
}

Propagate with ?

rust
fn process_file(path: &str) -> Result<Data, Error> {
    let content = fs::read_to_string(path)?;
    let parsed = serde_json::from_str(&content)?;
    let validated = validate(parsed)?;
    Ok(validated)
}

Structs and Enums

Builder Pattern

rust
#[derive(Default)]
pub struct ServerBuilder {
    port: Option<u16>,
    host: Option<String>,
}

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

    pub fn host(mut self, host: impl Into<String>) -> Self {
        self.host = Some(host.into());
        self
    }

    pub fn build(self) -> Result<Server, BuildError> {
        Ok(Server {
            port: self.port.unwrap_or(8080),
            host: self.host.unwrap_or_else(|| "localhost".into()),
        })
    }
}

Newtype Pattern

rust
pub struct UserId(u64);
pub struct Email(String);

impl Email {
    pub fn new(email: String) -> Result<Self, ValidationError> {
        if email.contains('@') {
            Ok(Self(email))
        } else {
            Err(ValidationError::InvalidEmail)
        }
    }
}

Enums for State

rust
enum ConnectionState {
    Disconnected,
    Connecting { attempt: u32 },
    Connected { session_id: String },
    Error { message: String },
}

Traits

Implement Standard Traits

rust
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct User {
    pub id: u64,
    pub name: String,
}

Trait Objects vs Generics

rust
// Static dispatch (monomorphization)
fn process<T: Handler>(handler: T) { ... }

// Dynamic dispatch (trait object)
fn process(handler: &dyn Handler) { ... }
fn process(handler: Box<dyn Handler>) { ... }

Extension Traits

rust
pub trait StringExt {
    fn truncate_ellipsis(&self, max_len: usize) -> String;
}

impl StringExt for str {
    fn truncate_ellipsis(&self, max_len: usize) -> String {
        if self.len() <= max_len {
            self.to_string()
        } else {
            format!("{}...", &self[..max_len - 3])
        }
    }
}

Lifetimes

Elision Rules

rust
// Lifetimes elided - single input reference
fn first_word(s: &str) -> &str { ... }

// Explicit when multiple inputs
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... }

Struct Lifetimes

rust
struct Parser<'a> {
    input: &'a str,
    position: usize,
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Self {
        Self { input, position: 0 }
    }
}

Async Rust

Tokio Patterns

rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result = fetch_data().await?;
    Ok(())
}

async fn fetch_data() -> Result<Data, Error> {
    let client = reqwest::Client::new();
    let response = client.get(URL).send().await?;
    response.json().await.map_err(Into::into)
}

Concurrent Operations

rust
use futures::future::join_all;

async fn fetch_all(urls: Vec<String>) -> Vec<Result<Response, Error>> {
    let futures: Vec<_> = urls.into_iter()
        .map(|url| fetch_one(url))
        .collect();

    join_all(futures).await
}

Testing

Unit Tests

rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    #[should_panic(expected = "division by zero")]
    fn test_divide_by_zero() {
        divide(1, 0);
    }
}

Integration Tests

rust
// tests/integration_test.rs
use mylib::process;

#[test]
fn test_full_workflow() {
    let result = process("input");
    assert!(result.is_ok());
}

Performance

Avoid Unnecessary Allocations

rust
// Bad - allocates new String
fn process(s: &str) -> String {
    s.to_uppercase()
}

// Good when possible - use Cow
use std::borrow::Cow;
fn process(s: &str) -> Cow<str> {
    if s.chars().any(|c| c.is_lowercase()) {
        Cow::Owned(s.to_uppercase())
    } else {
        Cow::Borrowed(s)
    }
}

Use Iterators

rust
// Good - lazy, zero-cost
let sum: i32 = numbers.iter()
    .filter(|n| **n > 0)
    .map(|n| n * 2)
    .sum();

Anti-Patterns to Avoid

  • .unwrap() in production code
  • Excessive .clone() to "fix" borrow checker
  • unsafe without clear justification
  • Ignoring compiler warnings
  • Not using clippy
  • Global mutable state (lazy_static with Mutex)

Expand your agent's capabilities with these related and highly-rated skills.

eous/dotclaude

typescript-best-practices

TypeScript development best practices, patterns, and conventions. Use when writing TypeScript code, reviewing .ts/.tsx files, discussing type safety, generics, utility types, or TypeScript project configuration. Triggers on mentions of TypeScript, tsconfig, type inference, generics, discriminated unions, React TypeScript.

0 0
Explore
eous/dotclaude

api-design

REST and GraphQL API design patterns, best practices, and conventions. Use when designing APIs, discussing HTTP methods, status codes, versioning, or API documentation. Triggers on mentions of REST, API, GraphQL, endpoint, HTTP methods, OpenAPI, swagger, API versioning.

0 0
Explore
eous/dotclaude

testing-patterns

Software testing patterns, strategies, and best practices. Use when writing tests, discussing test architecture, mocking, fixtures, or test organization. Triggers on mentions of testing, unit tests, integration tests, pytest, jest, mocking, fixtures, TDD, test coverage.

0 0
Explore
eous/dotclaude

security-checklist

Application security best practices and vulnerability prevention. Use when reviewing code for security issues, implementing authentication, or discussing OWASP vulnerabilities. Triggers on mentions of security, authentication, authorization, XSS, SQL injection, CSRF, OWASP, encryption, secrets.

0 0
Explore
eous/dotclaude

python-best-practices

Python development best practices, patterns, and conventions. Use when writing Python code, reviewing .py files, discussing pytest, asyncio, type hints, pydantic, dataclasses, or Python project structure. Triggers on mentions of Python, pytest, mypy, ruff, black, FastAPI, Django, Flask.

0 0
Explore
eous/dotclaude

memory-generator

Generate CLAUDE.md project memory files by exploring codebases. Use when user asks to "generate memory", "create CLAUDE.md", "document this project", "understand this codebase", or starts work on a new/unfamiliar repository. Triggers on new project onboarding and documentation requests.

0 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results