Claude Code Plugins

Community-maintained marketplace

Feedback

Expert knowledge for writing cutting-edge, idiomatic Rust code with Rust Edition 2024, strict clippy compliance, and functional-but-pragmatic philosophy. Use when writing any Rust code, fixing clippy warnings, structuring modules or crates, reviewing or refactoring Rust code, or questions about Rust 2024 features, async patterns, documentation standards, or performance optimization.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name modern-rust-expert
description Expert knowledge for writing cutting-edge, idiomatic Rust code with Rust Edition 2024, strict clippy compliance, and functional-but-pragmatic philosophy. Use when writing any Rust code, fixing clippy warnings, structuring modules or crates, reviewing or refactoring Rust code, or questions about Rust 2024 features, async patterns, documentation standards, or performance optimization.

Modern Rust Expert

Expert knowledge for writing cutting-edge, idiomatic Rust code with Rust edition 2024, strict clippy compliance, and functional-but-pragmatic philosophy.

When to Use This Skill

Automatically apply this knowledge when:

  • Writing any Rust code in this project
  • Fixing clippy warnings
  • Structuring new modules or crates
  • Reviewing or refactoring Rust code

Rust Edition 2024 Specifics

Version Requirements

  • Edition: 2024
  • MSRV: 1.85.0 (minimum for edition 2024)
  • Always check rustc --version and update with rustup update stable if needed

Edition 2024 & Rust 1.85+ Features Available

1. Async Functions in Traits (Stable!)

Use async fn directly in trait definitions without async-trait crate:

// Modern Rust - async fn in traits (no macro needed!)
trait Database: Send + Sync {
    async fn save(&self, data: &[u8]) -> Result<(), Error>;
    async fn load(&self, id: &str) -> Result<Vec<u8>, Error>;
}

// Implementation
impl Database for PostgresDb {
    async fn save(&self, data: &[u8]) -> Result<(), Error> {
        // Direct async implementation
        sqlx::query("INSERT INTO ...").execute(&self.pool).await?;
        Ok(())
    }
}

Use this for: All async trait methods in Environment traits (Database, HttpClient, etc.)

2. Return Position Impl Trait in Traits (RPITIT)

Return impl Trait from trait methods:

trait EventPublisher {
    // Can return impl Future instead of Box<dyn Future>
    fn publish(&self, event: Event) -> impl Future<Output = Result<()>> + Send;
}

Benefit: No heap allocation, better performance than Box<dyn Future>.

3. Let-Else Statements

Early return with pattern matching:

// Modern pattern for error handling
fn process_order(action: OrderAction) -> Result<Order, Error> {
    let OrderAction::PlaceOrder { customer_id, items } = action else {
        return Err(Error::InvalidAction);
    };

    // customer_id and items are in scope here
    Ok(Order { customer_id, items })
}

// Compare to old style:
fn process_order_old(action: OrderAction) -> Result<Order, Error> {
    match action {
        OrderAction::PlaceOrder { customer_id, items } => {
            Ok(Order { customer_id, items })
        }
        _ => Err(Error::InvalidAction),
    }
}

Use for: Extracting enum variants with early return.

4. Enhanced Const Generics

Use const generics in more contexts:

// Const generic for effect buffer size
struct EffectBuffer<Action, const N: usize> {
    effects: [Option<Effect<Action>>; N],
}

impl<Action, const N: usize> EffectBuffer<Action, N> {
    const fn new() -> Self {
        Self {
            effects: [const { None }; N],
        }
    }
}

Use for: Stack-allocated buffers with configurable size.

5. Inline Const Expressions

Use const { } blocks in const contexts:

const DEFAULT_CAPACITY: usize = 16;

struct Cache<T> {
    // Inline const expression
    data: [Option<T>; const { DEFAULT_CAPACITY * 2 }],
}

6. C-String Literals

Create CStr at compile time:

// Modern: c"string" literal
let path = c"/tmp/data";  // Type: &'static CStr

// Old way required:
use std::ffi::CString;
let path = CString::new("/tmp/data").unwrap();

Use for: FFI code and system calls.

7. If/While Let Chains

Combine multiple patterns with &&:

// Modern: let chains
fn handle_event(state: &State, action: Action) {
    if let OrderAction::PlaceOrder { items, .. } = action
        && !items.is_empty()
        && state.can_place_order()
    {
        // All conditions met
    }
}

// Old way required nested ifs or match

8. Improved Pattern Matching

Rest patterns in slices:

match events.as_slice() {
    [first, .., last] => {
        // first and last are available
    }
    _ => {}
}

Patterns in let statements:

let [first, second, ..] = &events[..] else {
    return Err(Error::NotEnoughEvents);
};

9. Expanded Const Fn Capabilities

More operations allowed in const fn:

const fn calculate_capacity(base: usize) -> usize {
    // These are all allowed in const fn now:
    if base < 16 { 16 } else { base }
}

const fn create_default<T>() -> Option<T> {
    None  // Can return generic types
}

// Use in const contexts
const CAPACITY: usize = calculate_capacity(10);

10. Precise Capturing in RPIT

Control which lifetimes are captured:

trait Store {
    // Only capture 'a, not all lifetimes
    fn get_state<'a>(&'a self) -> impl Future<Output = State> + use<'a>;
}

Features NOT Available (Require Nightly)

To avoid confusion, these are NOT in stable yet:

  • gen blocks for generators
  • ❌ Type alias impl trait (TAIT)
  • ❌ Specialization
  • ❌ Generic const expressions (full support)
  • #[derive] on enum with generics in some cases

Recommended Patterns for This Project

  1. Use async fn in traits - No more async-trait dependency
  2. Prefer let-else - Cleaner error handling
  3. Use RPITIT - Return impl Future instead of Box<dyn Future>
  4. Const generics for buffers - Stack allocation where possible
  5. Let chains - Combine multiple conditions elegantly

Clippy Configuration & Compliance

Workspace Lint Configuration

Always configure lints at workspace level in root Cargo.toml:

[workspace.lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"

[workspace.lints.clippy]
# Pedantic lints - USE LOWER PRIORITY so specific lints can override
pedantic = { level = "warn", priority = -1 }

# Deny common issues
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
todo = "deny"
unimplemented = "deny"

# Performance
missing_const_for_fn = "warn"

# Cognitive complexity
cognitive_complexity = "warn"

# Documentation
missing_errors_doc = "warn"
missing_panics_doc = "warn"

Critical: Lint groups like pedantic MUST have priority = -1 to avoid conflicts with specific lints.

Individual Crate Configuration

In each crate's Cargo.toml, inherit workspace lints:

[lints]
workspace = true

Common Clippy Issues & Solutions

1. Mixed Attributes Style

Problem: Having both outer (///) and inner (//!) doc comments on the same item.

Wrong:

/// Module for actions
///
/// This contains action types.
pub mod action {
    //! Action implementations
}

Correct:

/// Module for actions
///
/// This contains action types.
///
/// Action implementations.
pub mod action {}

Rule: Pick one style per item. For modules, use outer docs (///) and move inner content to the outer doc comment.

2. Documentation Backticks

Rule: ALL type names, function names, trait names in documentation MUST be in backticks.

Wrong:

/// - Database: The database trait
/// - HttpClient: HTTP client

Correct:

/// - `Database`: The database trait
/// - `HttpClient`: HTTP client

3. Missing # Panics Documentation

If a function can panic, document it:

/// Create a test clock
///
/// # Panics
///
/// Panics if the hardcoded timestamp fails to parse
/// (should never happen in practice).
#[allow(clippy::expect_used)]  // Justified in test utilities
pub fn test_clock() -> Clock {
    // ...
}

4. Non-Debug Types

Some types (like Future) don't implement Debug. Manually implement it:

pub enum Effect<Action> {
    Future(Pin<Box<dyn Future<Output = Option<Action>> + Send>>),
}

// Manual Debug implementation
impl<Action> std::fmt::Debug for Effect<Action>
where
    Action: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Effect::Future(_) => write!(f, "Effect::Future(<future>)"),
            // ... other arms
        }
    }
}

5. Missing const fn

If clippy suggests a function can be const, make it const:

// Clippy will suggest this can be const
pub const fn merge(effects: Vec<Effect<Action>>) -> Effect<Action> {
    Effect::Parallel(effects)
}

6. Wildcard Imports

Avoid wildcard imports in library code:

Wrong:

use super::*;

Correct:

use super::{Arc, Effect, Reducer, RwLock};

Exception: Wildcard imports are acceptable in tests and examples.

7. Match Arm Consistency

Consolidate identical match arms:

Wrong:

match effect {
    Effect::None => {},
    Effect::Parallel(_) => {},
    Effect::Sequential(_) => {},
}

Correct:

match effect {
    Effect::None | Effect::Parallel(_) | Effect::Sequential(_) => {
        // Placeholder implementation
    },
}

8. Doc List Indentation

Module docs must be indented or separated with blank lines:

Wrong:

pub mod mocks {
    //! Mock implementations
}

Correct (moved to outer):

/// Mock implementations
pub mod mocks {}

Rustfmt Configuration

Stable Features Only

Only use rustfmt features available in stable Rust. Many formatting options require nightly.

Safe rustfmt.toml:

edition = "2024"
max_width = 100
hard_tabs = false
tab_spaces = 4
newline_style = "Unix"
fn_params_layout = "Tall"
match_block_trailing_comma = true
chain_width = 60
use_try_shorthand = true
use_field_init_shorthand = true
force_explicit_abi = true

Avoid (require nightly):

  • imports_granularity
  • group_imports
  • wrap_comments
  • format_code_in_doc_comments
  • normalize_comments
  • reorder_impl_items
  • brace_style
  • match_arm_blocks

Functional-but-Pragmatic Philosophy

Core Principles

  1. Prefer immutability, but allow &mut self when performance matters
  2. Prefer pure functions, but recognize async/await patterns
  3. Prefer composition, but allow practical escape hatches
  4. Favor readability over theoretical purity

Practical Applications

Effect-as-Value Pattern

Describe side effects as values, don't execute them immediately:

// Good: Return effect description
fn reduce(&self, state: &mut State, action: Action, env: &Env) -> Vec<Effect> {
    vec![Effect::Database(SaveOrder), Effect::PublishEvent(event)]
}

// Bad: Execute side effects directly
fn reduce(&self, state: &mut State, action: Action, env: &Env) {
    env.database.save(state);  // ❌ Side effect in reducer!
}

Mutable State in Reducers

It's OK to mutate state in place in reducers for performance:

trait Reducer {
    fn reduce(
        &self,
        state: &mut Self::State,  // ✅ Mutable for performance
        action: Self::Action,
        env: &Self::Environment,
    ) -> Vec<Effect<Self::Action>>;
}

This is pragmatic: tests are still fast and deterministic, and we avoid unnecessary cloning.

Static Dispatch Over Dynamic

Prefer generic types (static dispatch) over trait objects (dynamic dispatch):

// Prefer this (static dispatch, zero-cost)
struct Store<S, A, E, R>
where
    R: Reducer<State = S, Action = A, Environment = E>
{
    reducer: R,
}

// Over this (dynamic dispatch, runtime cost)
struct Store<S, A, E> {
    reducer: Box<dyn Reducer<State = S, Action = A>>,
}

Exception: Use dynamic dispatch when you need runtime polymorphism (plugins, hot-swapping implementations).

Error Handling

Strict Rules:

  • NEVER use unwrap() or expect() in library code (deny by clippy)
  • NEVER use panic!() in library code (deny by clippy)
  • NEVER leave todo!() or unimplemented!() in production code (deny by clippy)

Exceptions (must be explicitly allowed):

// Test utilities can use expect with justification
#[allow(clippy::expect_used)]
pub fn test_clock() -> Clock {
    DateTime::parse_from_rfc3339("2025-01-01T00:00:00Z")
        .expect("hardcoded timestamp should always parse")
        .with_timezone(&Utc)
}

Always document why you're allowing it.

Code Organization Patterns

Module Documentation

Pattern 1: Small modules - Use outer docs only:

/// Action types and utilities
///
/// Actions represent all possible state transitions.
pub mod action {}

Pattern 2: Large modules - No inner docs if you have comprehensive outer docs:

/// Store runtime for coordinating reducer execution
///
/// Detailed explanation here.
///
/// Store runtime implementation details.
pub mod store {
    // No //! needed, everything is in outer docs
}

Re-exports

Use pub use for convenience, but document what you're re-exporting:

// Re-export commonly used types from dependencies
pub use chrono::{DateTime, Utc};
pub use serde::{Deserialize, Serialize};

// Re-export from submodules for convenience
pub use mocks::{FixedClock, test_clock};

Workspace Structure

For multi-crate projects:

project/
├── Cargo.toml          # Workspace root with [workspace.dependencies]
├── core/
│   ├── Cargo.toml      # Uses workspace.dependencies
│   └── src/lib.rs
├── runtime/
│   ├── Cargo.toml      # Depends on core
│   └── src/lib.rs
└── testing/
    ├── Cargo.toml      # Depends on core + runtime
    └── src/lib.rs

Rules:

  • Share dependencies via [workspace.dependencies]
  • Use dependency.workspace = true in crate manifests
  • Shared package metadata via [workspace.package]
  • Workspace lints via [workspace.lints]

Common Gotchas & Solutions

1. Workspace Dependencies Cannot Be Optional

Wrong:

[workspace.dependencies]
sqlx = { version = "0.8", optional = true }  # ❌ Error!

Correct:

[workspace.dependencies]
sqlx = { version = "0.8" }  # Define without optional

# In individual crate Cargo.toml:
[dependencies]
sqlx = { workspace = true, optional = true }  # ✅ Make it optional here

2. Edition 2024 Requires Newer MSRV

If you get:

error: rust-version 1.83.0 is older than first version (1.85.0) required by edition 2024

Solution: Update MSRV:

[workspace.package]
rust-version = "1.85.0"  # Minimum for edition 2024

3. Future Doesn't Implement Debug

Pin<Box> doesn't implement Debug. See "Non-Debug Types" section above for manual implementation.

4. Async Functions in Placeholders

Placeholders in async functions trigger unused_async:

#[allow(clippy::unused_async)]  // Justified: placeholder implementation
async fn execute_effect(&self, effect: Effect<A>) {
    // Will be implemented in Phase 1
}

Testing Patterns

Test Organization

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

    #[test]
    fn test_something() {
        // Unit test
    }

    #[tokio::test]
    async fn test_async_something() {
        // Async test
    }
}

Property-Based Testing

Use proptest for property tests:

use proptest::prelude::*;

proptest! {
    #[test]
    fn property_holds(input: Vec<u8>) {
        prop_assert!(check_property(&input));
    }
}

Performance Considerations

Zero-Cost Abstractions

With static dispatch, generics compile to optimal code:

// This generic code:
fn reduce<D: Database>(state: &mut State, db: &D) {
    db.save(state);
}

// Monomorphizes to:
fn reduce_with_postgres(state: &mut State, db: &PostgresDatabase) {
    db.save(state);  // Direct call, no vtable lookup
}

Allocation Minimization

In hot paths, consider SmallVec for small collections:

use smallvec::SmallVec;

// Stack-allocated for ≤ 4 items, heap for more
fn reduce(&self, ...) -> SmallVec<[Effect; 4]> {
    let mut effects = SmallVec::new();
    effects.push(Effect::Save);
    effects
}

Documentation Standards

Crate-Level Documentation

Start lib.rs with comprehensive module docs:

//! # Crate Name
//!
//! Brief description.
//!
//! ## Overview
//!
//! Detailed explanation.
//!
//! ## Example
//!
//! ```ignore
//! // Example code here
//! ```

Function Documentation

/// Brief one-line description
///
/// Longer detailed explanation if needed.
///
/// # Arguments
///
/// - `state`: Description
/// - `action`: Description
///
/// # Returns
///
/// What the function returns.
///
/// # Errors
///
/// When this function returns an error (if applicable).
///
/// # Panics
///
/// When this function panics (if applicable).
///
/// # Example
///
/// ```
/// // Example usage
/// ```
pub fn my_function(state: &State, action: Action) -> Result<(), Error> {
    // ...
}

Type Documentation

/// Brief description of the type
///
/// # Type Parameters
///
/// - `S`: State type
/// - `A`: Action type
///
/// # Example
///
/// ```
/// // Example usage
/// ```
pub struct MyType<S, A> {
    // ...
}

Quick Reference Checklist

Before committing Rust code:

  • Run cargo fmt --all
  • Run cargo clippy --all-targets --all-features -- -D warnings
  • Run cargo test --all-features
  • Run cargo doc --no-deps --all-features
  • All type names in docs have backticks
  • No unwrap/panic/todo in library code
  • Functions that panic have # Panics section
  • No wildcard imports (use super::*)
  • Manual Debug impl for non-Debug types
  • Const fn where applicable
  • Static dispatch preferred over dynamic

Common Commands

# Format code
cargo fmt --all

# Check without building
cargo check --all-features

# Build
cargo build --all-features

# Test
cargo test --all-features

# Lint
cargo clippy --all-targets --all-features -- -D warnings

# Documentation
cargo doc --no-deps --all-features --open

# Documentation with warnings as errors
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features

Project-Specific Guidelines

This Project's Philosophy

  1. Functional Core, Imperative Shell: Pure business logic, effects as values
  2. Explicit over Implicit: All side effects are visible
  3. Type Safety: Make invalid states unrepresentable
  4. Testability: Business logic tests run at memory speed
  5. Performance: Zero-cost abstractions via static dispatch
  6. Pragmatism: Functional patterns, but practical when needed

Architecture Patterns

  • Action: Unified type for commands and events
  • Reducer: Pure function (State, Action, Env) → (State, Effects)
  • Effect: Values describing side effects (not execution)
  • Environment: Trait-based dependency injection
  • Store: Runtime that coordinates everything

When in Doubt

  • Check the architecture spec: specs/architecture.md
  • Follow patterns from existing code
  • Prefer explicitness over cleverness
  • If clippy complains, there's usually a good reason
  • Ask for clarification rather than guessing

Remember: This skill is automatically applied to all Rust code in this project. Follow these guidelines to write idiomatic, performant, and maintainable Rust code.