Rust programming expert
Skill roedyrustam/claudevibeskills/src/rust-programming-expert
Koleksi 20 Claude Skills siap pakai untuk pengembangan SaaS, web modern, dan praktik rekayasa perangkat lunak tingkat lanjut.
npx -y skills add roedyrustam/claudevibeskills --skill rust-programming-expertAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Expert skill for Rust programming (Rust 2024 / v1.85+). Use whenever the user writes, reviews, or debugs Rust code — covering memory safety (ownership, lifetimes), async (Tokio, async closures), web backends (Axum, SQLx), CLI tools (Clap, Serde), unsafe code, performance optimization, and Cargo profiling. Trigger on any mention of Rust, Cargo, Tokio, Axum, SQLx, Clap, Serde, lifetimes, ownership, or Rust error handling. Also trigger for systems programming, WebAssembly, or high-performance service development in Rust.
SKILL.md
10.2 KB, as published. Nobody here has run it
Rust Programming Expert (2024 Edition / v1.85+)
Production Rust patterns: memory safety, async-first, web backends, CLI, and performance.
Project Setup
# New project
cargo new my-app --bin
cargo new my-lib --lib
# Essential Cargo.toml
[package]
name = "my-app"
version = "0.1.0"
edition = "2024"
[dependencies]
tokio = { version = "1", features = ["full"] }
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "uuid", "chrono"] }
anyhow = "1"
thiserror = "2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
[dev-dependencies]
tokio-test = "0.4"
Ownership & Borrowing Patterns
Common Ownership Patterns
// Clone when you need owned data in multiple places
fn process(data: Vec<String>) -> Vec<String> {
let upper: Vec<String> = data.iter().map(|s| s.to_uppercase()).collect();
upper // data dropped here
}
// Borrow when you only need to read
fn count_long(items: &[String], min_len: usize) -> usize {
items.iter().filter(|s| s.len() >= min_len).count()
}
// Cow<str> for "borrow or own" flexibility
use std::borrow::Cow;
fn sanitize(input: &str) -> Cow<str> {
if input.contains('<') {
Cow::Owned(input.replace('<', "<"))
} else {
Cow::Borrowed(input) // zero allocation when no change needed
}
}
Lifetime Annotations
// Named lifetime: output lives as long as input
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Struct holding a reference
struct Parser<'a> {
input: &'a str,
pos: usize,
}
impl<'a> Parser<'a> {
fn new(input: &'a str) -> Self {
Self { input, pos: 0 }
}
fn remaining(&self) -> &'a str {
&self.input[self.pos..]
}
}
Error Handling
thiserror for Library Errors
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Not found: {0}")]
NotFound(String),
#[error("Unauthorized")]
Unauthorized,
#[error("Validation failed: {field} — {message}")]
Validation { field: String, message: String },
#[error("Database error")]
Database(#[from] sqlx::Error),
#[error("IO error")]
Io(#[from] std::io::Error),
}
// Result alias
pub type Result<T> = std::result::Result<T, AppError>;
anyhow for Application Code
use anyhow::{Context, Result, bail, ensure};
async fn load_config(path: &str) -> Result<Config> {
let content = tokio::fs::read_to_string(path)
.await
.with_context(|| format!("Failed to read config file: {path}"))?;
let config: Config = serde_json::from_str(&content)
.context("Config file is not valid JSON")?;
ensure!(config.port > 1024, "Port must be > 1024, got {}", config.port);
Ok(config)
}
Axum Error Response
use axum::{http::StatusCode, response::{IntoResponse, Response}, Json};
use serde_json::json;
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized".into()),
AppError::Validation { field, message } => (
StatusCode::UNPROCESSABLE_ENTITY,
format!("{field}: {message}"),
),
AppError::Database(_) | AppError::Io(_) => {
tracing::error!(error = ?self, "Internal error");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".into())
}
};
(status, Json(json!({ "error": message }))).into_response()
}
}
Async with Tokio
Main Entry Point
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let config = load_config("config.json").await?;
let app = build_app(config).await?;
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
tracing::info!("Listening on {}", listener.local_addr()?);
axum::serve(listener, app).await?;
Ok(())
}
Concurrent Tasks
use tokio::task::JoinSet;
async fn fetch_all(ids: Vec<String>) -> Vec<Result<Data>> {
let mut set = JoinSet::new();
for id in ids {
set.spawn(async move { fetch_one(&id).await });
}
let mut results = Vec::new();
while let Some(res) = set.join_next().await {
results.push(res.expect("task panicked"));
}
results
}
// Or use join! for fixed set of futures
async fn fetch_dashboard(user_id: &str) -> Result<Dashboard> {
let (user, posts, stats) = tokio::try_join!(
fetch_user(user_id),
fetch_posts(user_id),
fetch_stats(user_id),
)?;
Ok(Dashboard { user, posts, stats })
}
Axum Web Backend
App Setup with State
use axum::{Router, extract::State, routing::{get, post}};
use std::sync::Arc;
#[derive(Clone)]
pub struct AppState {
pub db: sqlx::PgPool,
pub config: Arc<Config>,
}
pub fn build_router(state: AppState) -> Router {
Router::new()
.route("/health", get(health_check))
.nest("/api/v1", api_routes())
.with_state(state)
.layer(
tower::ServiceBuilder::new()
.layer(tower_http::trace::TraceLayer::new_for_http())
.layer(tower_http::cors::CorsLayer::permissive())
)
}
fn api_routes() -> Router<AppState> {
Router::new()
.route("/posts", get(list_posts).post(create_post))
.route("/posts/:id", get(get_post).delete(delete_post))
}
Handlers
use axum::{
extract::{Path, Query, State, Json},
http::StatusCode,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Deserialize)]
pub struct ListQuery {
page: Option<u32>,
limit: Option<u32>,
}
#[derive(Serialize)]
pub struct PostResponse {
id: Uuid,
title: String,
content: Option<String>,
}
pub async fn list_posts(
State(state): State<AppState>,
Query(query): Query<ListQuery>,
) -> Result<Json<Vec<PostResponse>>, AppError> {
let page = query.page.unwrap_or(1).max(1);
let limit = query.limit.unwrap_or(10).min(100);
let offset = (page - 1) * limit;
let posts = sqlx::query_as!(
PostResponse,
"SELECT id, title, content FROM posts WHERE published = true
ORDER BY created_at DESC LIMIT $1 OFFSET $2",
limit as i64,
offset as i64,
)
.fetch_all(&state.db)
.await?;
Ok(Json(posts))
}
#[derive(Deserialize)]
pub struct CreatePost {
title: String,
content: Option<String>,
}
pub async fn create_post(
State(state): State<AppState>,
Json(body): Json<CreatePost>,
) -> Result<(StatusCode, Json<PostResponse>), AppError> {
if body.title.trim().is_empty() {
return Err(AppError::Validation {
field: "title".into(),
message: "must not be empty".into(),
});
}
let post = sqlx::query_as!(
PostResponse,
"INSERT INTO posts (title, content) VALUES ($1, $2) RETURNING id, title, content",
body.title,
body.content,
)
.fetch_one(&state.db)
.await?;
Ok((StatusCode::CREATED, Json(post)))
}
CLI with Clap
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "myapp", about = "My CLI tool", version)]
pub struct Cli {
#[arg(short, long, default_value = "info")]
pub log_level: String,
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
/// Start the server
Serve {
#[arg(short, long, default_value = "3000")]
port: u16,
},
/// Run database migrations
Migrate,
/// Generate a secret key
GenKey,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Serve { port } => serve(port).await?,
Command::Migrate => migrate().await?,
Command::GenKey => println!("{}", generate_key()),
}
Ok(())
}
Performance Tips
// Pre-allocate when size is known
let mut vec = Vec::with_capacity(items.len());
// Use &str instead of String in hot paths
fn is_valid(s: &str) -> bool { /* no allocation */ }
// Avoid unnecessary clones — use references
fn process(items: &[Item]) -> usize { items.len() }
// Use rayon for CPU-bound parallelism
use rayon::prelude::*;
let sum: u64 = big_vec.par_iter().map(|x| expensive(x)).sum();
// Use bytes::Bytes for zero-copy HTTP bodies
use bytes::Bytes;
async fn handler() -> Bytes {
Bytes::from_static(b"hello")
}
// Profile with cargo-flamegraph
// cargo install flamegraph
// cargo flamegraph --bin my-app
Key Rules
- Prefer
Result<T, E>overunwrap()— onlyunwrap()in tests or truly infallible thiserrorfor libraries,anyhowfor binariesArc<T>for shared state,Mutex<T>only when mutation neededtokio::spawnfor fire-and-forget,JoinSetfor tracked taskssqlx::query_as!macro — compile-time query validation- Never block in async context — use
tokio::task::spawn_blockingfor CPU work tracingnotprintln!— structured logging always- Clippy is not optional —
cargo clippy -- -D warningsin CI #[derive(Clone)]on state — Axum requiresCloneon shared state- Edition 2024 in
Cargo.toml— use latest language features