Skip to main content
rust-project Modern Rust project architecture guide for 2025. Use when creating Rust projects (CLI, web services, libraries). Covers workspace structure, error handling, async patterns, and idiomatic Rust best practices.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/majiayu000/spellbook --skill rust-projectThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... Related occupations SOC
Based on SOC occupation classification
More from this repository Route coding or repository-review work so GPT-5.6 Sol remains the commander and verifier while a separate GPT-5.6 Luna Max Codex CLI session performs bounded implementation or read-only investigation. Use when the user asks for Sol to direct, plan, supervise, or review Luna work; when native Sol-to-Luna spawning is unavailable or incompatible; or when a task needs auditable agent ownership, budgeted verification, live progress, timeout recovery, usage telemetry, and a Sol review loop.
Create distinctive, production-grade web frontend interfaces with high design quality. Use when the user explicitly asks to build or substantially redesign a web component, page, or application. Only applies when repository inspection confirms that the visual surface is a browser-rendered web UI. Do not use for native or GPU-rendered graphics, shaders, games, terminal UIs, or other non-web visual systems. Ignore incidental mentions and traces.
Review whether a repository's coding-agent harness can reliably carry work from intent through controlled execution, verification, delivery, and learning. Use when asked to assess agent readiness, repeated agent failures, Rules/Skills/Hooks/Memory effectiveness, missing validation or recovery loops, or whether a harness repair improved later outcomes. Do not use for code-only audits, AGENTS-only audits, individual skill reliability reviews, or executing the task itself.
name rust-project description Modern Rust project architecture guide for 2025. Use when creating Rust projects (CLI, web services, libraries). Covers workspace structure, error handling, async patterns, and idiomatic Rust best practices.
Rust Project Architecture
Core Principles
Ownership-first โ Embrace borrow checker, no unnecessary clones
Zero-cost abstractions โ Newtype, iterators, async/await
Workspace for scale โ Use Cargo workspace for multi-crate projects
Error precision โ thiserror for libs, anyhow for apps
Async with Tokio โ Tokio runtime + tracing for observability
No backwards compatibility โ Delete, don't deprecate. Change directly
LiteLLM for LLM APIs โ Use LiteLLM proxy for all LLM integrations
No Backwards Compatibility
Delete unused code. Change directly. No compatibility layers.
#[deprecated(since = "0.2.0" , note = "Use new_function instead" )]
pub fn old_function () { ... }
pub type OldName = NewName;
fn (_legacy: & , data: &Data) { ... }
() { ... }
() { ... }
(data: &Data) { ... }
process
str
#[cfg(feature = "legacy" )]
fn
old_impl
pub
fn
new_function
fn
process
LiteLLM for LLM APIs
Use LiteLLM proxy. Don't call provider APIs directly.
use async_openai::{Client, config::OpenAIConfig};
pub fn create_client (base_url: &str , api_key: &str ) -> Client<OpenAIConfig> {
let config = OpenAIConfig::new ()
.with_api_base (base_url)
.with_api_key (api_key);
Client::with_config (config)
}
let client = create_client ("http://localhost:4000" , &api_key);
let request = CreateChatCompletionRequestArgs::default ()
.model ("gpt-4o" )
.messages (vec! [...])
.build ()?;
Quick Start
1. Initialize Project
cargo new myapp
cd myapp
mkdir myapp && cd myapp
cargo init --name app
2. Apply Tech Stack Layer Recommendation Async Runtime Tokio Web Framework Axum Serialization Serde ORM / Database SeaORM (async, Active Record) CLI Clap (derive) Error (lib) thiserror Error (app) anyhow Logging tracing + tracing-subscriber HTTP Client reqwest Config config-rs
Web Framework Selection Framework Choose When Axum (default)Modern microservices, Tokio ecosystem, container deployment, Tower middleware Actix Web Maximum throughput, WebSocket-heavy, mature ecosystem needed Rocket Rapid prototyping, small teams, minimal boilerplate
Axum provides the best balance of performance, ergonomics, and Tokio integration for most projects.
Database / ORM Selection Library Choose When SeaORM (default)CRUD-heavy services, rapid development, async-first, cross-database testing SQLx Raw SQL control, maximum performance, compile-time SQL validation Diesel Compile-time type safety, stable schema, synchronous workloads
SeaORM is recommended for its Active Record ergonomics, native async support, and seamless Axum integration.
Version Strategy
Always use latest. Never pin in templates.
[dependencies]
tokio = { version = "*" , features = ["full" ] }
axum = "*"
serde = { version = "*" , features = ["derive" ] }
3. Choose Project Structure
Simple Project (Single Crate) myapp/
โโโ Cargo.toml
โโโ src/
โ โโโ main.rs # Entry point
โ โโโ lib.rs # Library root (optional)
โ โโโ config.rs # Configuration
โ โโโ error.rs # Error types
โ โโโ handlers/ # HTTP handlers (web)
โ โ โโโ mod.rs
โ โโโ services/ # Business logic
โ โ โโโ mod.rs
โ โโโ models/ # Domain types
โ โโโ mod.rs
โโโ tests/ # Integration tests
โ โโโ api_test.rs
โโโ benches/ # Benchmarks
โโโ bench.rs
Workspace Project (Multi-Crate) myapp/
โโโ Cargo.toml # Workspace manifest
โโโ crates/
โ โโโ app/ # Binary crate
โ โ โโโ Cargo.toml
โ โ โโโ src/main.rs
โ โโโ core/ # Business logic lib
โ โ โโโ Cargo.toml
โ โ โโโ src/lib.rs
โ โโโ infra/ # Infrastructure lib
โ โโโ Cargo.toml
โ โโโ src/lib.rs
โโโ config/
โ โโโ default.toml
โโโ Makefile
Architecture Layers
main.rs โ Entry Point Wire dependencies, start runtime. No business logic.
use anyhow::Result ;
use sea_orm::Database;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main () -> Result <()> {
tracing_subscriber::registry ()
.with (tracing_subscriber::fmt::layer ())
.init ();
let config = myapp::config::load ()?;
let db = Database::connect (&config.database_url).await ?;
let state = myapp::AppState::new (db);
let app = myapp::router::build (state);
let listener = tokio::net::TcpListener::bind (&config.listen_addr).await ?;
tracing::info!("listening on {}" , config.listen_addr);
axum::serve (listener, app).await ?;
Ok (())
}
lib.rs โ Library Root Re-export public API, define AppState.
pub mod config;
pub mod db;
pub mod error;
pub mod handlers;
pub mod models;
pub mod router;
pub mod services;
use sea_orm::DatabaseConnection;
use std::sync::Arc;
pub struct AppState {
pub db: DatabaseConnection,
}
impl AppState {
pub fn new (db: DatabaseConnection) -> Arc<Self > {
Arc::new (Self { db })
}
}
error.rs โ Error Handling
use axum::{http::StatusCode, response::{IntoResponse, Response}, Json};
use sea_orm::DbErr;
use serde_json::json;
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("not found: {0}" )]
NotFound (String ),
#[error("validation error: {0}" )]
Validation (String ),
#[error("unauthorized" )]
Unauthorized,
#[error("internal error" )]
Internal (#[from] anyhow::Error),
#[error("database error: {0}" )]
Database (#[from] DbErr),
}
impl IntoResponse for AppError {
fn into_response (self ) -> Response {
let (status, message) = match &self {
AppError::NotFound (msg) => (StatusCode::NOT_FOUND, msg.clone ()),
AppError::Validation (msg) => (StatusCode::BAD_REQUEST, msg.clone ()),
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized" .into ()),
AppError::Internal (_) | AppError::Database (_) => {
tracing::error!("Internal error: {:?}" , self );
(StatusCode::INTERNAL_SERVER_ERROR, "internal error" .into ())
}
};
(status, Json (json!({ "error" : message }))).into_response ()
}
}
pub type Result <T> = std::result::Result <T, AppError>;
handlers/ โ HTTP Layer
use axum::{extract::{Path, State}, Json};
use std::sync::Arc;
use crate::{error::Result , models::user, services, AppState};
pub async fn get_user (
State (state): State<Arc<AppState>>,
Path (id): Path<i64 >,
) -> Result <Json<user::Model>> {
let user = services::user::find_by_id (&state.db, id).await ?;
Ok (Json (user))
}
pub async fn create_user (
State (state): State<Arc<AppState>>,
Json (input): Json<CreateUserInput>,
) -> Result <Json<user::Model>> {
let user = services::user::create (&state.db, input).await ?;
Ok (Json (user))
}
services/ โ Business Logic
use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set};
use crate::{error::{AppError, Result }, models::user};
pub async fn find_by_id (db: &DatabaseConnection, id: i64 ) -> Result <user::Model> {
user::Entity::find_by_id (id)
.one (db)
.await ?
.ok_or_else (|| AppError::NotFound (format! ("user {}" , id)))
}
pub async fn create (db: &DatabaseConnection, input: CreateUserInput) -> Result <user::Model> {
let new_user = user::ActiveModel {
email: Set (input.email),
name: Set (input.name),
..Default ::default ()
};
let user = new_user.insert (db).await ?;
Ok (user)
}
pub async fn find_with_posts (db: &DatabaseConnection, id: i64 ) -> Result <(user::Model, Vec <post::Model>)> {
user::Entity::find_by_id (id)
.find_with_related (post::Entity)
.all (db)
.await ?
.into_iter ()
.next ()
.ok_or_else (|| AppError::NotFound (format! ("user {}" , id)))
}
Workspace Configuration
[workspace]
resolver = "3"
members = ["crates/*" ]
[workspace.package]
version = "0.1.0"
edition = "2024"
license = "MIT"
[workspace.dependencies]
tokio = { version = "*" , features = ["full" ] }
axum = "*"
serde = { version = "*" , features = ["derive" ] }
serde_json = "*"
sea-orm = { version = "*" , features = ["sqlx-postgres" , "runtime-tokio-native-tls" ] }
thiserror = "*"
anyhow = "*"
tracing = "*"
tracing-subscriber = "*"
[package]
name = "app"
version.workspace = true
edition.workspace = true
[dependencies]
core.path = "../core"
infra.path = "../infra"
tokio.workspace = true
axum.workspace = true
anyhow.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
CLI Application
use clap::Parser;
use anyhow::Result ;
#[derive(Parser)]
#[command(name = "myapp" , version, about)]
struct Cli {
#[arg(short, long)]
input: PathBuf,
#[arg(short, long, default_value = "json" )]
format: OutputFormat,
#[arg(short, long)]
verbose: bool ,
}
#[derive(Clone, clap::ValueEnum)]
enum OutputFormat {
Json,
Yaml,
Text,
}
fn main () -> Result <()> {
let cli = Cli::parse ();
if cli.verbose {
tracing_subscriber::fmt::init ();
}
Ok (())
}
Testing
use axum::{body::Body, http::{Request, StatusCode}};
use tower::ServiceExt;
#[tokio::test]
async fn test_get_user () {
let app = create_test_app ().await ;
let response = app
.oneshot (
Request::builder ()
.uri ("/users/1" )
.body (Body::empty ())
.unwrap (),
)
.await
.unwrap ();
assert_eq! (response.status (), StatusCode::OK);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_email () {
assert! (validate_email ("test@example.com" ).is_ok ());
assert! (validate_email ("invalid" ).is_err ());
}
}
Extended Reference Detailed material starting at ## Makefile has been moved to reference/extended.md to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.