Rust development workflow with quality gates, testing, and iteration patterns. Use when developing Rust code, running tests, or iterating on Rust projects.
Install
npx skillscat add thrashr888/thrashr888-agent-kit/rust-development Install via the SkillsCat registry.
SKILL.md
Rust Development Workflow
Development patterns for Rust CLI tools and libraries, based on AllBeads and QDOS workflows.
Quality Gates
ALL THREE must pass before commits:
cargo fmt -- --check && cargo clippy -- -D warnings && cargo testRun frequently during development, not just before commits.
Development Loop
1. Check and Build
# Fast check (no codegen)
cargo check
# Full build
cargo build
# Release build
cargo build --release2. Run Tests
# All tests
cargo test
# Specific test
cargo test test_name
# With output
cargo test -- --nocapture
# Single-threaded (for tests with shared state)
cargo test -- --test-threads=13. Format and Lint
# Format code
cargo fmt
# Check formatting without changing
cargo fmt -- --check
# Lint with warnings as errors
cargo clippy -- -D warnings
# Auto-fix clippy suggestions
cargo clippy --fix --allow-dirty4. Run the Binary
# Debug build
cargo run -- [args]
# Release build
cargo run --release -- [args]
# With specific features
cargo run --features "feature1,feature2" -- [args]Project Structure
Standard Rust project layout:
project/
├── Cargo.toml # Package manifest
├── Cargo.lock # Dependency lock
├── src/
│ ├── main.rs # Binary entry point
│ ├── lib.rs # Library root (if both bin and lib)
│ └── module/
│ └── mod.rs
├── tests/ # Integration tests
├── benches/ # Benchmarks
└── examples/ # Example binariesError Handling Patterns
Application Errors (use anyhow)
use anyhow::{Context, Result};
fn main() -> Result<()> {
let config = load_config()
.context("Failed to load configuration")?;
Ok(())
}Library Errors (use thiserror)
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MyError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}Dependency Management
# Add dependency
cargo add serde --features derive
# Add dev dependency
cargo add --dev tokio-test
# Update dependencies
cargo update
# Check for outdated
cargo outdated # requires cargo-outdatedWorkspace Setup
For multi-crate projects:
# Cargo.toml (workspace root)
[workspace]
members = ["crate1", "crate2"]
resolver = "2"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }Performance Profiling
# Build with debug info for profiling
cargo build --release
# Time compilation
cargo build --timings
# Check binary size
cargo bloat --release # requires cargo-bloatBuild Performance
Use mold linker (Linux)
Faster linking times:
# .cargo/config.toml
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-fuse-ld=mold"]Install: sudo apt install mold or brew install mold
Use sccache for caching
Cache compiler artifacts across builds:
cargo install --locked sccache
export RUSTC_WRAPPER=sccache
# Add to shell profile for persistence
echo 'export RUSTC_WRAPPER=sccache' >> ~/.zshrcAuto-derive macro
Reduce boilerplate for common derives:
macro_rules! auto_derived {
( $( $item:item )+ ) => {
$(
#[derive(Debug, Clone, PartialEq, Eq)]
$item
)+
};
}
// Usage
auto_derived! {
pub struct User {
id: i64,
name: String,
}
pub struct Config {
host: String,
port: u16,
}
}Add Serialize, Deserialize to the macro if using serde.
Common Patterns
Builder Pattern
pub struct Config {
host: String,
port: u16,
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
#[derive(Default)]
pub struct ConfigBuilder {
host: Option<String>,
port: Option<u16>,
}
impl ConfigBuilder {
pub fn host(mut self, host: impl Into<String>) -> Self {
self.host = Some(host.into());
self
}
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn build(self) -> Result<Config, &'static str> {
Ok(Config {
host: self.host.ok_or("host required")?,
port: self.port.unwrap_or(8080),
})
}
}Newtype Pattern
pub struct UserId(pub i64);
pub struct Email(String);
impl Email {
pub fn new(s: impl Into<String>) -> Result<Self, &'static str> {
let s = s.into();
if s.contains('@') {
Ok(Self(s))
} else {
Err("invalid email")
}
}
}IDE Integration
For VS Code with rust-analyzer:
// .vscode/settings.json
{
"rust-analyzer.check.command": "clippy",
"rust-analyzer.cargo.features": "all"
}Anti-Patterns
DON'T:
- Use
.unwrap()in library code (use?instead) - Ignore clippy warnings
- Skip
cargo fmtbefore commits - Write tests after the fact
DO:
- Write code that passes fmt/clippy on first write
- Use
Result<T, E>for fallible operations - Run tests frequently during development
- Use
#[must_use]for important return values