Skip to content
Back to skills

Rust

ASecurity

Rust ownership model, async programming with Tokio, web frameworks (Axum/Actix), error handling, and systems programming patterns

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 26, 2026
databasesrustgosqlnodenodejstestingapidatabasebackendperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 26, 2026

npx -y skills add nhan03bit/ultrathink --skill rust --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rust?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rust
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nhan03bit-rust/badge)](https://www.skillsdirectory.com/skills/nhan03bit-rust)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rust
description: Rust ownership model, async programming with Tokio, web frameworks (Axum/Actix), error handling, and systems programming patterns
layer: domain
category: backend
triggers:
  - "rust"
  - "cargo"
  - "tokio"
  - "axum"
  - "actix"
  - "ownership"
  - "borrow checker"
  - "rust web"
inputs:
  - "Rust application requirements"
  - "Performance-critical system needs"
  - "Web service specifications"
outputs:
  - "Idiomatic Rust implementations"
  - "Async patterns with Tokio"
  - "Web framework configurations"
linksTo:
  - microservices
  - postgresql
  - redis
  - websockets
  - message-queues
linkedFrom:
  - error-handling
  - testing
preferredNextSkills:
  - postgresql
  - redis
  - microservices
fallbackSkills:
  - golang
  - nodejs
riskLevel: low
memoryReadPolicy: selective
memoryWritePolicy: none
sideEffects: []
---

# Rust Domain Skill

## Purpose

Provide expert-level guidance on Rust development, including the ownership system, async programming with Tokio, web framework patterns (Axum, Actix), error handling with `thiserror`/`anyhow`, and production-grade systems programming.

## Key Patterns

### 1. Ownership and Borrowing

```rust
// Ownership rules:
// 1. Each value has exactly one owner
// 2. When the owner goes out of scope, the value is dropped
// 3. You can have EITHER one mutable reference OR any number of immutable references

// Prefer borrowing over cloning
fn process_name(name: &str) -> String {
    // Takes a reference -- no ownership transfer
    name.to_uppercase()
}

// Use lifetimes when the compiler needs help
struct Config<'a> {
    database_url: &'a str,
    app_name: &'a str,
}

impl<'a> Config<'a> {
    fn connection_string(&self) -> String {
        format!("{}?app={}", self.database_url, self.app_name)
    }
}

// Interior mutability for shared mutable state
use std::sync::{Arc, RwLock};

#[derive(Clone)]
struct AppState {
    cache: Arc<RwLock<HashMap<String, CachedItem>>>,
    db: Arc<Pool<Postgres>>,
}

impl AppState {
    fn get_cached(&self, key: &str) -> Option<CachedItem> {
        self.cache.read().unwrap().get(key).cloned()
    }

    fn set_cached(&self, key: String, item: CachedItem) {
        self.cache.write().unwrap().insert(key, item);
    }
}
```

### 2. Error Handling

```rust
use thiserror::Error;

// Domain-specific errors with thiserror
#[derive(Error, Debug)]
pub enum AppError {
    #[error("resource not found: {resource} with id {id}")]
    NotFound { resource: String, id: String },

    #[error("unauthorized: {0}")]
    Unauthorized(String),

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

    #[error("database error")]
    Database(#[from] sqlx::Error),

    #[error("external service error")]
    ExternalService(#[from] reqwest::Error),

    #[error(transparent)]
    Internal(#[from] anyhow::Error),
}

// Map to HTTP responses (Axum)
impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, code, message) = match &self {
            AppError::NotFound { .. } => (StatusCode::NOT_FOUND, "NOT_FOUND", self.to_string()),
            AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, "UNAUTHORIZED", msg.clone()),
            AppError::Validation(msg) => (StatusCode::UNPROCESSABLE_ENTITY, "VALIDATION", msg.clone()),
            AppError::Database(_) => {
                tracing::error!(error = ?self, "database error");
                (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL", "internal error".into())
            }
            AppError::ExternalService(_) => {
                tracing::error!(error = ?self, "external service error");
                (StatusCode::BAD_GATEWAY, "BAD_GATEWAY", "upstream error".into())
            }
            AppError::Internal(_) => {
                tracing::error!(error = ?self, "internal error");
                (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL", "internal error".into())
            }
        };

        (status, Json(json!({ "error": { "code": code, "message": message } }))).into_response()
    }
}

// Use Result<T, AppError> throughout
type AppResult<T> = Result<T, AppError>;
```

### 3. Axum Web Framework

```rust
use axum::{
    extract::{Path, Query, State, Json},
    http::StatusCode,
    middleware,
    routing::{get, post, delete},
    Router,
};
use tower::ServiceBuilder;
use tower_http::{
    cors::CorsLayer,
    trace::TraceLayer,
    compression::CompressionLayer,
    timeout::TimeoutLayer,
};

pub fn create_router(state: AppState) -> Router {
    let api = Router::new()
        .route("/users", get(list_users).post(create_user))
        .route("/users/{id}", get(get_user).delete(delete_user))
        .layer(middleware::from_fn_with_state(state.clone(), auth_middleware));

    Router::new()
        .nest("/api/v1", api)
        .route("/health", get(health_check))
        .layer(
            ServiceBuilder::new()
                .layer(TraceLayer::new_for_http())
                .layer(CompressionLayer::new())
                .layer(TimeoutLayer::new(Duration::from_secs(30)))
                .layer(CorsLayer::permissive()),
        )
        .with_state(state)
}

// Handler with extractors
async fn list_users(
    State(state): State<AppState>,
    Query(params): Query<ListUsersParams>,
) -> AppResult<Json<PaginatedResponse<UserResponse>>> {
    let users = state.user_service.list(params.into()).await?;
    Ok(Json(users))
}

async fn create_user(
    State(state): State<AppState>,
    Json(body): Json<CreateUserRequest>,
) -> AppResult<(StatusCode, Json<UserResponse>)> {
    let user = state.user_service.create(body).await?;
    Ok((StatusCode::CREATED, Json(user)))
}

// Middleware
async fn auth_middleware(
    State(state): State<AppState>,
    mut req: Request,
    next: Next,
) -> Result<Response, AppError> {
    let token = req
        .headers()
        .get("Authorization")
        .and_then(|h| h.to_str().ok())
        .and_then(|h| h.strip_prefix("Bearer "))
        .ok_or_else(|| AppError::Unauthorized("missing token".into()))?;

    let claims = state.auth_service.verify(token).await?;
    req.extensions_mut().insert(claims);
    Ok(next.run(req).await)
}
```

### 4. Async with Tokio

```rust
use tokio::sync::{mpsc, Semaphore};
use tokio::time::{timeout, Duration};

// Bounded channel for backpressure
async fn process_events(mut rx: mpsc::Receiver<Event>) {
    while let Some(event) = rx.recv().await {
        if let Err(e) = handle_event(&event).await {
            tracing::error!(error = ?e, event = ?event, "failed to handle event");
        }
    }
}

// Semaphore for concurrency limiting
async fn fetch_all(urls: Vec<String>, max_concurrent: usize) -> Vec<Result<String, reqwest::Error>> {
    let semaphore = Arc::new(Semaphore::new(max_concurrent));
    let client = reqwest::Client::new();

    let tasks: Vec<_> = urls
        .into_iter()
        .map(|url| {
            let sem = semaphore.clone();
            let client = client.clone();
            tokio::spawn(async move {
                let _permit = sem.acquire().await.unwrap();
                client.get(&url).send().await?.text().await
            })
        })
        .collect();

    let mut results = Vec::with_capacity(tasks.len());
    for task in tasks {
        results.push(task.await.unwrap());
    }
    results
}

// Graceful shutdown
async fn run_server(listener: TcpListener, app: Router) {
    let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();

    tokio::spawn(async move {
        let mut sigterm = tokio::signal::unix::signal(SignalKind::terminate()).unwrap();
        tokio::select! {
            _ = tokio::signal::ctrl_c() => {}
            _ = sigterm.recv() => {}
        }
        let _ = shutdown_tx.send(());
    });

    axum::serve(listener, app)
        .with_graceful_shutdown(async { shutdown_rx.await.ok(); })
        .await
        .unwrap();
}
```

### 5. Database with SQLx

```rust
use sqlx::{PgPool, FromRow, query_as};

#[derive(FromRow)]
struct User {
    id: Uuid,
    email: String,
    name: String,
    created_at: chrono::DateTime<chrono::Utc>,
}

// Compile-time checked queries
async fn get_user(pool: &PgPool, id: Uuid) -> AppResult<User> {
    sqlx::query_as!(
        User,
        r#"SELECT id, email, name, created_at FROM users WHERE id = $1"#,
        id
    )
    .fetch_optional(pool)
    .await?
    .ok_or_else(|| AppError::NotFound {
        resource: "user".into(),
        id: id.to_string(),
    })
}

// Transactions
async fn transfer_credits(pool: &PgPool, from: Uuid, to: Uuid, amount: i64) -> AppResult<()> {
    let mut tx = pool.begin().await?;

    sqlx::query!("UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1", amount, from)
        .execute(&mut *tx)
        .await?;

    sqlx::query!("UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, to)
        .execute(&mut *tx)
        .await?;

    tx.commit().await?;
    Ok(())
}
```

## Best Practices

1. **Use `clippy`** with `#![deny(clippy::all)]` in CI
2. **Prefer `&str` over `String`** in function parameters
3. **Use `thiserror`** for library errors, `anyhow` for application errors
4. **Use `tracing`** instead of `log` for structured, async-aware logging
5. **Prefer `Arc<T>` over `Rc<T>`** unless single-threaded is guaranteed
6. **Use `tokio::spawn`** for CPU-bound work in `spawn_blocking`
7. **Prefer iterators** over manual loops -- they often optimize better
8. **Use `#[derive]` macros** extensively: `Debug, Clone, PartialEq, Serialize, Deserialize`
9. **Test with `#[tokio::test]`** for async tests
10. **Use `cargo deny`** to audit dependencies for vulnerabilities

## Common Pitfalls

| Pitfall | Impact | Fix |
|---------|--------|-----|
| Holding `MutexGuard` across `.await` | Deadlock | Scope the lock, clone data out |
| `unwrap()` in production code | Panics | Use `?` operator, `expect()` with context |
| Cloning everything to satisfy borrow checker | Performance degradation | Restructure code, use `Arc`, references |
| Blocking the Tokio runtime | Async task starvation | Use `tokio::task::spawn_blocking` |
| Not setting `#[tokio::main(flavor = "multi_thread")]` | Single-threaded runtime | Explicitly configure runtime |
| Unbounded channels | Memory exhaustion | Use `mpsc::channel(capacity)` |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.