All skills
jeffallan avatar

/rust-engineer

@efebc44
by jeffallanjeffallan/claude-skills12k stars
1,124

Writes, reviews, and debugs idiomatic Rust code with memory safety and zero-cost abstractions. Implements ownership patterns, manages lifetimes, designs trait hierarchies, builds async applications with tokio, and structures error handling with Result/Option. Use when building Rust applications, solving ownership or borrowing issues, designing trait-based APIs, implementing async/await concurrency, creating FFI bindings, or optimizing for performance and memory safety. Invoke for Rust, Cargo, ownership, borrowing, lifetimes, async Rust, tokio, zero-cost abstractions, memory safety, systems programming.

Use this Skill: https://skilld.dev/gh/jeffallan/claude-skills/rust-engineer

This session only. Nothing lands on disk.

referencestesting.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Testing in Rust

Unit Tests

// Tests in same file
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_addition() {
        assert_eq!(2 + 2, 4);
    }

    #[test]
    fn test_subtraction() {
        assert!(10 - 5 == 5);
    }

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

    #[test]
    fn test_result() -> Result<(), String> {
        let result = divide(10, 2)?;
        assert_eq!(result, 5);
        Ok(())
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // Run with: cargo test -- --ignored
    }
}

// Assertions
fn assert_examples() {
    assert!(true);
    assert_eq!(2 + 2, 4);
    assert_ne!(2 + 2, 5);

    // Custom messages
    assert!(value > 0, "Value must be positive, got {}", value);
    assert_eq!(result, expected, "Calculation failed");
}

Doctests

/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// use mylib::add;
///
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
///
/// ```should_panic
/// use mylib::divide;
///
/// divide(10, 0);  // This will panic
/// ```
///
/// ```ignore
/// // This code won't compile but won't fail the test
/// let x = undefined_function();
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

Integration Tests

// tests/integration_test.rs
use mylib;

#[test]
fn test_full_workflow() {
    let config = mylib::Config::new("test.conf");
    let result = mylib::process(&config);
    assert!(result.is_ok());
}

// tests/common/mod.rs - shared test utilities
pub fn setup() -> TestContext {
    TestContext {
        db: create_test_db(),
    }
}

// tests/another_test.rs
mod common;

#[test]
fn test_with_common() {
    let ctx = common::setup();
    // Use ctx...
}

Test Organization

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

    mod addition {
        use super::*;

        #[test]
        fn positive_numbers() {
            assert_eq!(add(2, 3), 5);
        }

        #[test]
        fn negative_numbers() {
            assert_eq!(add(-2, -3), -5);
        }
    }

    mod subtraction {
        use super::*;

        #[test]
        fn test_subtract() {
            assert_eq!(subtract(10, 5), 5);
        }
    }
}

Test Fixtures and Setup

struct TestContext {
    temp_dir: std::path::PathBuf,
    db: Database,
}

impl TestContext {
    fn setup() -> Self {
        let temp_dir = std::env::temp_dir().join("test");
        std::fs::create_dir_all(&temp_dir).unwrap();

        Self {
            temp_dir,
            db: Database::connect_test(),
        }
    }
}

impl Drop for TestContext {
    fn drop(&mut self) {
        // Cleanup
        std::fs::remove_dir_all(&self.temp_dir).ok();
        self.db.disconnect();
    }
}

#[test]
fn test_with_fixture() {
    let ctx = TestContext::setup();
    // Test uses ctx...
    // Automatic cleanup via Drop
}

Async Tests

use tokio;

#[tokio::test]
async fn test_async_function() {
    let result = async_operation().await;
    assert_eq!(result, 42);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_with_custom_runtime() {
    let result = concurrent_operation().await;
    assert!(result.is_ok());
}

// Testing async with timeout
#[tokio::test]
async fn test_with_timeout() {
    let timeout = std::time::Duration::from_secs(5);
    let result = tokio::time::timeout(timeout, slow_operation()).await;
    assert!(result.is_ok());
}

Property-Based Testing (proptest)

use proptest::prelude::*;

// Simple property test
proptest! {
    #[test]
    fn test_reversing_twice_is_identity(ref s in ".*") {
        let reversed: String = s.chars().rev().collect();
        let double_reversed: String = reversed.chars().rev().collect();
        assert_eq!(s, &double_reversed);
    }
}

// Custom strategies
proptest! {
    #[test]
    fn test_addition_commutative(a in 0..1000i32, b in 0..1000i32) {
        assert_eq!(a + b, b + a);
    }

    #[test]
    fn test_vector_push_pop(
        ref v in prop::collection::vec(0..100i32, 0..100),
        item in 0..100i32
    ) {
        let mut v = v.clone();
        v.push(item);
        assert_eq!(v.pop(), Some(item));
    }
}

// Complex custom strategies
fn user_strategy() -> impl Strategy<Value = User> {
    (1..1000u64, "[a-z]{3,10}", "[a-z0-9.]+@[a-z]+\\.[a-z]+")
        .prop_map(|(id, name, email)| User { id, name, email })
}

proptest! {
    #[test]
    fn test_user_serialization(user in user_strategy()) {
        let json = serde_json::to_string(&user).unwrap();
        let deserialized: User = serde_json::from_str(&json).unwrap();
        assert_eq!(user, deserialized);
    }
}

Mocking

// Using mockall
use mockall::*;
use mockall::predicate::*;

#[automock]
trait Database {
    fn get_user(&self, id: u64) -> Option<User>;
    fn save_user(&mut self, user: User) -> Result<(), Error>;
}

#[test]
fn test_with_mock() {
    let mut mock = MockDatabase::new();

    mock.expect_get_user()
        .with(eq(1))
        .times(1)
        .returning(|_| Some(User { id: 1, name: "Alice".to_string() }));

    mock.expect_save_user()
        .times(1)
        .returning(|_| Ok(()));

    // Use mock in test
    let user = mock.get_user(1);
    assert!(user.is_some());
}

Benchmarks (Criterion)

// benches/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn fibonacci(n: u64) -> u64 {
    match n {
        0 => 1,
        1 => 1,
        n => fibonacci(n - 1) + fibonacci(n - 2),
    }
}

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

// Cargo.toml:
// [dev-dependencies]
// criterion = "0.5"
//
// [[bench]]
// name = "my_benchmark"
// harness = false

Advanced Benchmarking

use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};

fn bench_multiple_sizes(c: &mut Criterion) {
    let mut group = c.benchmark_group("sorting");

    for size in [10, 100, 1000, 10000].iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
            b.iter_batched(
                || generate_random_vec(size),
                |mut v| v.sort(),
                criterion::BatchSize::SmallInput,
            );
        });
    }

    group.finish();
}

// Comparing implementations
fn bench_comparison(c: &mut Criterion) {
    let mut group = c.benchmark_group("string_search");

    group.bench_function("naive", |b| {
        b.iter(|| naive_search(black_box("haystack"), black_box("needle")))
    });

    group.bench_function("optimized", |b| {
        b.iter(|| optimized_search(black_box("haystack"), black_box("needle")))
    });

    group.finish();
}

criterion_group!(benches, bench_multiple_sizes, bench_comparison);
criterion_main!(benches);

Testing with External Resources

// Testing file I/O
#[test]
fn test_file_operations() {
    use std::io::Write;

    let temp_dir = std::env::temp_dir();
    let file_path = temp_dir.join("test_file.txt");

    // Write
    let mut file = std::fs::File::create(&file_path).unwrap();
    file.write_all(b"test content").unwrap();

    // Read
    let content = std::fs::read_to_string(&file_path).unwrap();
    assert_eq!(content, "test content");

    // Cleanup
    std::fs::remove_file(&file_path).unwrap();
}

// Testing with databases (using sqlx)
#[sqlx::test]
async fn test_database_operations(pool: sqlx::PgPool) -> sqlx::Result<()> {
    sqlx::query("INSERT INTO users (name) VALUES ($1)")
        .bind("Alice")
        .execute(&pool)
        .await?;

    let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
        .fetch_one(&pool)
        .await?;

    assert_eq!(count.0, 1);
    Ok(())
}

Snapshot Testing

// Using insta crate
use insta::assert_snapshot;

#[test]
fn test_output_format() {
    let data = generate_complex_output();
    assert_snapshot!(data);
}

#[test]
fn test_json_output() {
    let json = serde_json::to_string_pretty(&get_data()).unwrap();
    assert_snapshot!(json);
}

// Run with: cargo insta test
// Review snapshots: cargo insta review

Code Coverage

// Using tarpaulin
// cargo install cargo-tarpaulin
// cargo tarpaulin --out Html --output-dir coverage

// Using llvm-cov
// cargo install cargo-llvm-cov
// cargo llvm-cov --html

Fuzzing

// Using cargo-fuzz
// cargo install cargo-fuzz
// cargo fuzz init

// fuzz/fuzz_targets/fuzz_target_1.rs
#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = mylib::parse_input(s);
    }
});

// Run with: cargo fuzz run fuzz_target_1

Best Practices

  • Write tests alongside production code in #[cfg(test)] modules
  • Use integration tests in tests/ directory for end-to-end testing
  • Include doctests in documentation for examples that must work
  • Use descriptive test names that explain what is being tested
  • Test edge cases (empty inputs, max values, etc.)
  • Use property-based testing for algorithmic code
  • Benchmark performance-critical code with criterion
  • Run tests in CI with cargo test --all-features
  • Use cargo test -- --nocapture to see println! output
  • Test error conditions with #[should_panic] or Result
  • Mock external dependencies for unit tests
  • Use test fixtures for complex setup/teardown
  • Run clippy on test code too
  • Measure code coverage and aim for high coverage
  • Use fuzzing for security-critical parsers
  • Test async code with tokio::test
  • Use snapshot testing for complex output validation

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub16d

    The skill provides comprehensive instructions for Rust engineering tasks. Security analysis identifies risks associated with the execution of testing commands and the handling of untrusted code, which are inherent to the skill's purpose. An external documentation link is provided which has been flagged by automated scanners, though it appears to be a legitimate vendor resource.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    6 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at efebc44. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 5 months ago
Other metadata
metadata
{
  "author": "https://github.com/Jeffallan",
  "version": "1.1.0",
  "domain": "language",
  "triggers": "Rust, Cargo, ownership, borrowing, lifetimes, async Rust, tokio, zero-cost abstractions, memory safety, systems programming",
  "role": "specialist",
  "scope": "implementation",
  "output-format": "code",
  "related-skills": "test-master"
}
  • Rust
  • cargo
  • ownership
  • lifetimes
  • async
  • tokio
  • traits
  • error-handling
  • systems-programming
  • memory-safety

README badge

README badge for jeffallan/claude-skills/rust-engineer

Writes, reviews, and debugs idiomatic Rust code with emphasis on ownership patterns, lifetime management, trait design, and async concurrency using tokio. Handles error propagation via Result/Option, validates against clippy and rustfmt, and minimizes unsafe code with documented safety invariants.

Generated from the current SKILL.md.

Does this skill handle async Rust and tokio?
Yes. The skill covers async/await patterns, tokio runtime setup, spawning concurrent tasks, and proper error handling in async contexts. It includes examples of tokio::join! and reqwest integration.
Will this skill write unsafe code?
The skill minimizes unsafe code and requires documentation of safety invariants for every unsafe block. It treats unsafe as a last resort and prefers type-system guarantees.
What error handling approach does this skill use?
The skill uses Result/Option with the ? operator and thiserror for custom error types. It avoids unwrap() in production code in favor of expect() with descriptive messages.
Does this skill validate code before returning it?
Yes. The skill runs cargo clippy, cargo fmt, and cargo test, and fixes all warnings before finalizing.
Can this skill help with lifetime and borrowing issues?
Yes. The skill analyzes ownership relationships, annotates lifetimes explicitly where needed, and designs borrowing patterns to leverage Rust's memory safety without cloning.

Generated from the current SKILL.md. These answers refresh after source changes.