hdb:rust-dev
Develop Rust code with practices that minimize compile-wait time and maximize throughput in AI-assisted workflows.
Usage
/hdb:rust-dev <task description>
Description
Implements Rust code using a batch-first workflow optimized for AI-assisted development. Instead of the naive write-one-file-compile-fix loop, this skill writes internally consistent code across multiple files before triggering a single compile pass, then fixes all errors in one batch. This approach eliminates the dominant time cost in AI-assisted Rust development: waiting for the compiler.
Instructions
When the user invokes /hdb:rust-dev <task description>:
Phase 1: Understand the task
Read relevant existing code. Before writing anything, read every file that will be modified or that the new code depends on. Understand the types, traits, module structure, and error handling patterns already in use.
Identify the full scope. List all files that need to be created or modified. Group them by dependency order:
- Leaf modules — types, models, data structures (no internal dependencies)
- Core logic — algorithms, business logic (depends on leaf modules)
- Integration points — handlers, CLI wiring, tests (depends on core logic)
Verify third-party crate APIs before writing code that uses them. For any crate you haven't used recently or any unfamiliar feature (template filters, integration crates, macro attributes):
- Check the docs for your exact version combination — e.g.,
askama 0.12 + axum 0.8 may not be compatible with askama_axum 0.4
- If an integration crate bridges two dependencies, verify all three versions are compatible before writing any handlers or templates
- When in doubt, write a minimal standalone example (
examples/smoke.rs) and cargo check it before building on the API
Identify domain-specific constraints and edge cases. Before writing core logic, document the domain invariants that the compiler cannot check:
- Sign conventions and ordering of operands in domain formulas
- Numerical edge cases (division by zero, trig inputs outside valid ranges, limits as values approach zero or infinity)
- Unit conversions and coordinate systems
- Business rules or domain constraints that produce wrong answers (not compiler errors) when violated
These domain bugs are invisible to the compiler and typically cost more debugging time than type errors.
Phase 2: Batch write
Write all code before compiling. Generate all files in dependency order (leaves first, integration last). Ensure internal consistency across files:
- Type names, field names, and method signatures match at every call site
- Imports reference the correct module paths
- Trait implementations satisfy all required methods
- Error types propagate consistently through
? chains
- Lifetimes and ownership are correct at API boundaries
Do not run cargo check or cargo build between files. The goal is zero intermediate compilations.
Self-review before compiling. Before triggering the first compile, scan the generated code for these common issues:
Rust-specific:
- Missing
use imports
- Mismatched
&str vs String at function boundaries
move closures that should borrow, or borrows that need clone()
- Missing
derive attributes (Debug, Clone, Serialize, etc.)
async functions that need .await or missing Send bounds
- Public vs private visibility (
pub, pub(crate))
Domain-specific:
- Do formulas match the reference specification? (sign conventions, operand order, edge cases)
- Are trig/math inputs clamped to valid ranges? (e.g.,
acos argument within [-1, 1])
- Are division-by-zero and degenerate cases handled? (e.g., guard against zero denominators)
- Do string format specifiers match the template engine's actual syntax? (e.g., Askama filter syntax vs
format! syntax)
Phase 3: Compile and fix
Use cargo check for the first pass, not cargo build. cargo check skips codegen and linking, running 2-3x faster. It catches all type errors, borrow errors, and lifetime issues.
cargo check 2>&1
Fix all errors in a single batch. Read the full compiler output, identify every error, and fix them all before recompiling. Do not fix one error and recompile — that wastes a full compile cycle on partial progress.
Common batch-fix patterns:
- If multiple files have the same import error, fix them all at once with parallel edits
- If a type rename caused errors across 5 files, fix all 5 before recompiling
- If the borrow checker rejects a pattern, fix the API design (not just the one call site) to prevent cascading errors
Iterate until clean. Repeat the check-fix cycle. Each cycle should resolve multiple errors. If a cycle fixes only one error, you are being too incremental — look for the root cause.
Run cargo build only when cargo check is clean and you need to execute the binary or run tests.
Run cargo test to verify correctness. If tests fail, fix the failures and re-run. Use cargo test -- --nocapture when you need to see output from failing tests.
Phase 4: Validate
Run clippy for lint issues.
cargo clippy 2>&1
Fix any warnings. Clippy catches idiomatic issues that cargo check misses.
Run cargo fmt --check to verify formatting. Apply cargo fmt if needed.
Build Optimization Reference
Apply these project-level optimizations when setting up a new Rust project or when build times become painful:
Fast linker (macOS Apple Silicon)
Add to .cargo/config.toml:
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/ld64.lld"]
Requires: brew install lld. On macOS the linker must be invoked as ld64.lld (not lld), which is the Mach-O compatible driver. Using plain lld will fail with "Invoke ld64.lld (macOS) instead". Cuts link time 50-80% on incremental builds.
Compilation caching
cargo install sccache
export RUSTC_WRAPPER=sccache
Caches compiled crates across builds. Saves time when switching branches, after cargo clean, or across projects sharing dependencies.
Workspace splitting
For projects with independent subsystems, split into a Cargo workspace:
[workspace]
members = ["core", "web", "cli"]
Benefits:
- Independent crates compile in parallel across CPU cores
- Only the changed crate recompiles on incremental builds
- Enforces clean API boundaries between subsystems
Split when: the project has 3+ modules with no circular dependencies and build times exceed 30 seconds.
Check tests without running them
cargo check --tests
Validates that test code compiles without building the test harness or running tests. Useful during the write phase when you want to verify test code is structurally correct.
Continuous checking during manual development
cargo watch -x check
Reruns cargo check on every file save. Useful when the developer is editing code manually between AI-assisted sessions.
Release Profile
For production binaries, add this to Cargo.toml to produce small, optimized, stripped binaries:
[profile.release]
codegen-units = 1 # Better optimization, slower compile
debug = false
lto = true
opt-level = "z" # Optimize for size
panic = "abort" # Don't include unwinding code
strip = true # Strip symbols from binary
What each setting does:
codegen-units = 1 — Allows LLVM to optimize across the entire crate as one unit. Produces faster/smaller code at the cost of slower release builds. Only affects cargo build --release.
lto = true — Link-Time Optimization across all crates. Eliminates dead code and inlines across crate boundaries. Significant size reduction.
opt-level = "z" — Optimize aggressively for binary size over speed. Use "3" instead if runtime performance matters more than binary size.
panic = "abort" — Removes unwinding machinery (~10-20% size reduction). Panics terminate immediately. Incompatible with catch_unwind() — only use in applications, not libraries.
strip = true — Strips debug symbols and symbol tables from the final binary.
When to use: CLI tools, web servers, deployable binaries. Do not apply panic = "abort" to library crates that may be used by others.
Rust-Specific Patterns
Error handling
- Use
anyhow::Result for application code and CLI tools
- Use
thiserror for library crates that expose typed errors
- Propagate with
? rather than .unwrap() in non-test code
- In tests,
.unwrap() is acceptable — it produces clear panic messages with line numbers
anyhow = "1.0"
thiserror = "2"
API design
- Use enums instead of boolean flags or boolean tuples. Replace
(bool, bool) parameter pairs with a named enum. ScrapeTargets::Both is self-documenting; (true, false) is not.
- Use
StatusCode with error responses in web handlers. Don't return error HTML without a corresponding HTTP status code.
Ownership at API boundaries
Design function signatures to minimize ownership friction:
- Accept
&str not String when the function doesn't need to store the value
- Accept
impl Into<String> when the function stores the value and callers might have either &str or String
- Return owned types (
String, Vec<T>) from functions — let the caller decide to borrow
- Use
Cow<'_, str> only when profiling shows the clone matters
Module organization
- Use
lib.rs + main.rs split for all non-trivial projects. Put all logic in lib.rs (and its submodules); main.rs only parses args and calls into the library. This is the single most impactful structural decision: it enables integration tests in tests/, which cannot import from a binary crate.
- One
mod.rs (or module_name.rs) per logical subsystem
- Re-export the public API from
mod.rs so callers use short paths (e.g., use crate::bemt::design_propeller not use crate::bemt::optimizer::design_propeller)
- Keep
mod.rs files thin — orchestration and re-exports, not implementation
- Unit tests go in the same file as the code they test (
#[cfg(test)] mod tests)
- Integration tests go in
tests/. These test the public API through use your_crate::.... Use test fixtures (files in tests/fixtures/) for data-driven tests. This is only possible with the lib.rs split.
Dependency management
- Pin major versions in
Cargo.toml (e.g., serde = "1" not serde = "*")
- Use
features sparingly — only enable what you need (e.g., tokio = { version = "1", features = ["rt-multi-thread", "macros"] } not features = ["full"])
- Prefer
bundled feature for C library bindings (e.g., rusqlite = { features = ["bundled"] }) to avoid system dependency issues
- Run
cargo update periodically to pick up patch releases
Preferred Crates by Domain
When the project has no existing precedent for a dependency, prefer these crates:
Command-line utilities
clap = { version = "4.3", features = ["derive"] } # Argument parsing with derive macros
dirs = "5.0" # Platform-standard directories (~/.config, etc.)
glob = "0.3" # File path glob matching
regex = "1.8" # Regular expressions
clap with derive feature for declarative argument definitions. Avoid hand-parsing std::env::args.
dirs for locating config/data/cache directories portably. Never hardcode ~/.config — it differs on macOS and Windows.
glob for file pattern matching (e.g., "src/**/*.rs").
regex is the standard regex engine. Compiles patterns to efficient automata. Use RegexSet when matching against multiple patterns.
Web applications
axum = "0.8" # Web framework (async, tower-based)
tokio = { version = "1.40", features = ["full"] } # Async runtime
tower-http = { version = "0.6", features = ["fs"] } # HTTP middleware (static files, CORS, etc.)
reqwest = { version = "0.12", features = ["rustls-tls"] } # HTTP client
askama = "0.12" # Compile-time HTML templates
Asynchronous operation
tokio = { version = "1.40", features = ["full"] } # Async runtime, timers, I/O, channels
features = ["full"] enables everything (runtime, macros, net, fs, time, sync). For libraries, enable only what you need: ["rt-multi-thread", "macros"].
- Prefer
tokio::spawn for concurrent tasks, tokio::select! for racing futures.
- Use
tokio::sync::Mutex (not std::sync::Mutex) when holding a lock across .await points.
System code with hashing and parallel execution
blake3 = { version = "1.8", features = ["rayon"] } # Fast cryptographic hashing (SIMD-accelerated)
rayon = "1.10" # Data parallelism (parallel iterators)
memmap2 = "0.9" # Memory-mapped file I/O
blake3 with rayon feature enables multi-threaded hashing of large files. Faster than SHA-256 for all input sizes.
rayon turns .iter() into .par_iter() for trivial parallelism. Use for CPU-bound work over collections. Do not mix with tokio — rayon has its own thread pool.
memmap2 for zero-copy access to large files. Avoids reading entire files into memory.
WASM (WebAssembly)
yew = { version = "0.21", features = ["csr"] } # Component framework (React-like)
patternfly-yew = "0.6" # PatternFly UI components for Yew
yew with csr (client-side rendering) for browser-targeted WASM applications.
patternfly-yew provides pre-built UI components (tables, forms, navigation) following the PatternFly design system.
- Build with
trunk serve for development, trunk build --release for production.
Serialization and deserialization
serde = { version = "1", features = ["derive"] } # Serialization framework
serde_json = "1" # JSON
serde_yaml = "0.9" # YAML
toml = "0.8" # TOML (config files)
csv = "1.3" # CSV reading/writing
chrono = { version = "0.4", features = ["serde"] } # DateTime with serde support
- Always enable
serde's derive feature. Use #[derive(Serialize, Deserialize)] on all data types that cross serialization boundaries.
chrono with serde feature for serializable timestamps. Use chrono::DateTime<Utc> as the standard time type.
- For TOML config files, prefer
toml crate over serde_toml.
Terminal / TUI applications
ratatui = "0.29" # TUI framework (widgets, layout, rendering)
crossterm = "0.28" # Terminal manipulation backend
ratatui is the actively maintained fork of tui-rs. Provides widgets (tables, lists, charts, paragraphs) and a layout system.
crossterm is the cross-platform terminal backend. Use with ratatui: ratatui::prelude::CrosstermBackend.
- Pattern: initialize terminal in
main(), restore on exit (including panic). Use std::panic::set_hook to ensure terminal cleanup.
Git operations
git2 = "0.19" # libgit2 bindings
git2 provides full git operations (clone, commit, diff, log, blame) without shelling out to git.
- Requires
libgit2 (bundled by default via libgit2-sys). No system dependency needed.
- For simple operations (status, add, commit), shelling out to
git via std::process::Command is simpler and avoids the compile-time cost of git2.
Crate Compatibility
When using multiple crates that integrate with each other, verify version compatibility before writing application code:
- Integration crates (e.g.,
askama_axum, tower-http, sqlx with runtime features) bridge two or more dependencies. All bridged versions must be compatible. Check the integration crate's Cargo.toml for its dependency version requirements.
- Test compatibility early. After adding a new integration crate, run
cargo check on a minimal use before writing handlers or business logic. Discovering incompatibility after writing 500 lines of handler code wastes the entire batch.
- When an integration crate lags behind its dependencies, drop it and implement the glue manually. For example, if a template integration crate doesn't support the latest version of your web framework, render templates manually and wrap the output. A few lines of manual glue is better than pinning to an old framework version.
- Pin integration crate versions explicitly (e.g.,
askama_axum = "=0.4.0") when you need a specific compatible combination, to prevent cargo update from breaking it.
Testing Strategies
Golden-value tests for numerical and domain code
For code that computes numerical results (solvers, financial calculations, data transformations), compile-time correctness is necessary but not sufficient — the code can compile and produce wrong answers. Use golden-value tests:
- Obtain reference values from a known-good source (published tables, reference implementation, manual calculation)
- Create test fixtures with input data and expected outputs
- Assert with tolerances — use approximate comparison for floating-point results:
assert!((result - expected).abs() < 1e-6, "expected {expected}, got {result}");
- Test edge cases explicitly — zero inputs, boundary values, degenerate cases that are valid but extreme
Integration tests with fixtures
For code that processes external data (HTML, files, API responses):
- Store representative fixtures in
tests/fixtures/ — real-world examples, not hand-crafted minimal inputs
- Test the public API end-to-end — parse, transform, and verify the output in a single test
- Include malformed inputs — test that bad data produces clear errors, not panics
Web application state
Prefer simpler state patterns that avoid ownership complexity:
- Pass configuration (not connections) in web state. For example, store a database path as a
String and open a connection per request, rather than sharing Arc<Mutex<Connection>> across handlers. This eliminates lock contention and simplifies ownership.
- Use
Arc<T> for truly shared read-only state (config, compiled templates, static data)
- Use per-request resources for anything with mutable state or cleanup requirements
Guidelines
- Batch over incremental. The single most impactful practice is writing more code before compiling. Each compile cycle costs 10-30 seconds; eliminating 10 unnecessary cycles saves 2-5 minutes per task.
- Read before writing. Never modify a file you haven't read. The compiler errors from misunderstanding existing types cost more time than reading the file would have.
- Fix root causes, not symptoms. If the borrow checker rejects a pattern in 3 places, the API design is wrong — fix the signature, not the call sites.
- Keep the dependency tree shallow. Every new crate dependency adds compile time. Check if the standard library or an existing dependency already provides the functionality.
- Use the type system, don't fight it. If you're writing a lot of
.clone(), Rc, or unsafe, step back and reconsider the data ownership model.
- Verify crate APIs before committing to them. The cost of discovering an API mismatch after writing 10 handlers is far higher than testing one minimal example first. This applies especially to template engines, integration crates, and anything with macro-based DSLs.
- Domain bugs cost more than type bugs. The compiler catches type errors, borrow errors, and lifetime issues. It cannot catch wrong formulas, incorrect sign conventions, or numerical edge cases. Invest verification effort proportional to the risk: domain-critical code needs golden-value tests, not just
cargo check.
- Split
lib.rs from main.rs by default. This is a one-time structural decision that enables integration testing, benchmarking, and reuse. Do it at project creation, not as a refactor later.
- Respect the user's CLAUDE.md. The user's global instructions override defaults. Check for project-specific conventions before applying generic Rust patterns.
Other
- Some AI LLMs suggest changing the Rust edition in Cargo.toml to 2021 from 2024. Their reasoning is mistaken: they are not up to date.The latest Rust edition is 2024 and Cargo.toml files with this should not be changed.
1---2name: hdb-rust-dev3description: Develop Rust code efficiently by minimizing compile cycles and batching work4---56# hdb:rust-dev78Develop Rust code with practices that minimize compile-wait time and maximize throughput in AI-assisted workflows.910## Usage1112```13/hdb:rust-dev <task description>14```1516## Description1718Implements Rust code using a batch-first workflow optimized for AI-assisted development. Instead of the naive write-one-file-compile-fix loop, this skill writes internally consistent code across multiple files before triggering a single compile pass, then fixes all errors in one batch. This approach eliminates the dominant time cost in AI-assisted Rust development: waiting for the compiler.1920## Instructions2122When the user invokes `/hdb:rust-dev <task description>`:2324### Phase 1: Understand the task25261. **Read relevant existing code.** Before writing anything, read every file that will be modified or that the new code depends on. Understand the types, traits, module structure, and error handling patterns already in use.27282. **Identify the full scope.** List all files that need to be created or modified. Group them by dependency order:29 - **Leaf modules** — types, models, data structures (no internal dependencies)30 - **Core logic** — algorithms, business logic (depends on leaf modules)31 - **Integration points** — handlers, CLI wiring, tests (depends on core logic)32333. **Verify third-party crate APIs before writing code that uses them.** For any crate you haven't used recently or any unfamiliar feature (template filters, integration crates, macro attributes):34 - Check the docs for your **exact version combination** — e.g., `askama 0.12` + `axum 0.8` may not be compatible with `askama_axum 0.4`35 - If an integration crate bridges two dependencies, verify all three versions are compatible before writing any handlers or templates36 - When in doubt, write a minimal standalone example (`examples/smoke.rs`) and `cargo check` it before building on the API37384. **Identify domain-specific constraints and edge cases.** Before writing core logic, document the domain invariants that the compiler cannot check:39 - Sign conventions and ordering of operands in domain formulas40 - Numerical edge cases (division by zero, trig inputs outside valid ranges, limits as values approach zero or infinity)41 - Unit conversions and coordinate systems42 - Business rules or domain constraints that produce **wrong answers** (not compiler errors) when violated4344 These domain bugs are invisible to the compiler and typically cost more debugging time than type errors.4546### Phase 2: Batch write47485. **Write all code before compiling.** Generate all files in dependency order (leaves first, integration last). Ensure internal consistency across files:49 - Type names, field names, and method signatures match at every call site50 - Imports reference the correct module paths51 - Trait implementations satisfy all required methods52 - Error types propagate consistently through `?` chains53 - Lifetimes and ownership are correct at API boundaries5455 **Do not run `cargo check` or `cargo build` between files.** The goal is zero intermediate compilations.56576. **Self-review before compiling.** Before triggering the first compile, scan the generated code for these common issues:5859 **Rust-specific:**60 - Missing `use` imports61 - Mismatched `&str` vs `String` at function boundaries62 - `move` closures that should borrow, or borrows that need `clone()`63 - Missing `derive` attributes (Debug, Clone, Serialize, etc.)64 - `async` functions that need `.await` or missing `Send` bounds65 - Public vs private visibility (`pub`, `pub(crate)`)6667 **Domain-specific:**68 - Do formulas match the reference specification? (sign conventions, operand order, edge cases)69 - Are trig/math inputs clamped to valid ranges? (e.g., `acos` argument within `[-1, 1]`)70 - Are division-by-zero and degenerate cases handled? (e.g., guard against zero denominators)71 - Do string format specifiers match the template engine's actual syntax? (e.g., Askama filter syntax vs `format!` syntax)7273### Phase 3: Compile and fix74757. **Use `cargo check` for the first pass, not `cargo build`.** `cargo check` skips codegen and linking, running 2-3x faster. It catches all type errors, borrow errors, and lifetime issues.7677 ```bash78 cargo check 2>&179 ```80818. **Fix all errors in a single batch.** Read the full compiler output, identify every error, and fix them all before recompiling. Do not fix one error and recompile — that wastes a full compile cycle on partial progress.8283 Common batch-fix patterns:84 - If multiple files have the same import error, fix them all at once with parallel edits85 - If a type rename caused errors across 5 files, fix all 5 before recompiling86 - If the borrow checker rejects a pattern, fix the API design (not just the one call site) to prevent cascading errors87889. **Iterate until clean.** Repeat the check-fix cycle. Each cycle should resolve multiple errors. If a cycle fixes only one error, you are being too incremental — look for the root cause.899010. **Run `cargo build` only when `cargo check` is clean** and you need to execute the binary or run tests.919211. **Run `cargo test` to verify correctness.** If tests fail, fix the failures and re-run. Use `cargo test -- --nocapture` when you need to see output from failing tests.9394### Phase 4: Validate959612. **Run clippy for lint issues.**9798 ```bash99 cargo clippy 2>&1100 ```101102 Fix any warnings. Clippy catches idiomatic issues that `cargo check` misses.10310413. **Run `cargo fmt --check`** to verify formatting. Apply `cargo fmt` if needed.105106## Build Optimization Reference107108Apply these project-level optimizations when setting up a new Rust project or when build times become painful:109110### Fast linker (macOS Apple Silicon)111112Add to `.cargo/config.toml`:113114```toml115[target.aarch64-apple-darwin]116rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/ld64.lld"]117```118119Requires: `brew install lld`. On macOS the linker must be invoked as `ld64.lld` (not `lld`), which is the Mach-O compatible driver. Using plain `lld` will fail with "Invoke ld64.lld (macOS) instead". Cuts link time 50-80% on incremental builds.120121### Compilation caching122123```bash124cargo install sccache125export RUSTC_WRAPPER=sccache126```127128Caches compiled crates across builds. Saves time when switching branches, after `cargo clean`, or across projects sharing dependencies.129130### Workspace splitting131132For projects with independent subsystems, split into a Cargo workspace:133134```toml135[workspace]136members = ["core", "web", "cli"]137```138139Benefits:140- Independent crates compile in parallel across CPU cores141- Only the changed crate recompiles on incremental builds142- Enforces clean API boundaries between subsystems143144Split when: the project has 3+ modules with no circular dependencies and build times exceed 30 seconds.145146### Check tests without running them147148```bash149cargo check --tests150```151152Validates that test code compiles without building the test harness or running tests. Useful during the write phase when you want to verify test code is structurally correct.153154### Continuous checking during manual development155156```bash157cargo watch -x check158```159160Reruns `cargo check` on every file save. Useful when the developer is editing code manually between AI-assisted sessions.161162## Release Profile163164For production binaries, add this to `Cargo.toml` to produce small, optimized, stripped binaries:165166```toml167[profile.release]168codegen-units = 1 # Better optimization, slower compile169debug = false170lto = true171opt-level = "z" # Optimize for size172panic = "abort" # Don't include unwinding code173strip = true # Strip symbols from binary174```175176**What each setting does:**177- `codegen-units = 1` — Allows LLVM to optimize across the entire crate as one unit. Produces faster/smaller code at the cost of slower release builds. Only affects `cargo build --release`.178- `lto = true` — Link-Time Optimization across all crates. Eliminates dead code and inlines across crate boundaries. Significant size reduction.179- `opt-level = "z"` — Optimize aggressively for binary size over speed. Use `"3"` instead if runtime performance matters more than binary size.180- `panic = "abort"` — Removes unwinding machinery (~10-20% size reduction). Panics terminate immediately. Incompatible with `catch_unwind()` — only use in applications, not libraries.181- `strip = true` — Strips debug symbols and symbol tables from the final binary.182183**When to use:** CLI tools, web servers, deployable binaries. Do not apply `panic = "abort"` to library crates that may be used by others.184185## Rust-Specific Patterns186187### Error handling188189- Use `anyhow::Result` for application code and CLI tools190- Use `thiserror` for library crates that expose typed errors191- Propagate with `?` rather than `.unwrap()` in non-test code192- In tests, `.unwrap()` is acceptable — it produces clear panic messages with line numbers193194```toml195anyhow = "1.0"196thiserror = "2"197```198199### API design200201- **Use enums instead of boolean flags or boolean tuples.** Replace `(bool, bool)` parameter pairs with a named enum. `ScrapeTargets::Both` is self-documenting; `(true, false)` is not.202- **Use `StatusCode` with error responses in web handlers.** Don't return error HTML without a corresponding HTTP status code.203204### Ownership at API boundaries205206Design function signatures to minimize ownership friction:207208- Accept `&str` not `String` when the function doesn't need to store the value209- Accept `impl Into<String>` when the function stores the value and callers might have either `&str` or `String`210- Return owned types (`String`, `Vec<T>`) from functions — let the caller decide to borrow211- Use `Cow<'_, str>` only when profiling shows the clone matters212213### Module organization214215- **Use `lib.rs` + `main.rs` split for all non-trivial projects.** Put all logic in `lib.rs` (and its submodules); `main.rs` only parses args and calls into the library. This is the single most impactful structural decision: it enables integration tests in `tests/`, which cannot import from a binary crate.216- One `mod.rs` (or `module_name.rs`) per logical subsystem217- Re-export the public API from `mod.rs` so callers use short paths (e.g., `use crate::bemt::design_propeller` not `use crate::bemt::optimizer::design_propeller`)218- Keep `mod.rs` files thin — orchestration and re-exports, not implementation219- Unit tests go in the same file as the code they test (`#[cfg(test)] mod tests`)220- **Integration tests go in `tests/`.** These test the public API through `use your_crate::...`. Use test fixtures (files in `tests/fixtures/`) for data-driven tests. This is only possible with the `lib.rs` split.221222### Dependency management223224- Pin major versions in `Cargo.toml` (e.g., `serde = "1"` not `serde = "*"`)225- Use `features` sparingly — only enable what you need (e.g., `tokio = { version = "1", features = ["rt-multi-thread", "macros"] }` not `features = ["full"]`)226- Prefer `bundled` feature for C library bindings (e.g., `rusqlite = { features = ["bundled"] }`) to avoid system dependency issues227- Run `cargo update` periodically to pick up patch releases228229## Preferred Crates by Domain230231When the project has no existing precedent for a dependency, prefer these crates:232233### Command-line utilities234235```toml236clap = { version = "4.3", features = ["derive"] } # Argument parsing with derive macros237dirs = "5.0" # Platform-standard directories (~/.config, etc.)238glob = "0.3" # File path glob matching239regex = "1.8" # Regular expressions240```241242- `clap` with `derive` feature for declarative argument definitions. Avoid hand-parsing `std::env::args`.243- `dirs` for locating config/data/cache directories portably. Never hardcode `~/.config` — it differs on macOS and Windows.244- `glob` for file pattern matching (e.g., `"src/**/*.rs"`).245- `regex` is the standard regex engine. Compiles patterns to efficient automata. Use `RegexSet` when matching against multiple patterns.246247### Web applications248249```toml250axum = "0.8" # Web framework (async, tower-based)251tokio = { version = "1.40", features = ["full"] } # Async runtime252tower-http = { version = "0.6", features = ["fs"] } # HTTP middleware (static files, CORS, etc.)253reqwest = { version = "0.12", features = ["rustls-tls"] } # HTTP client254askama = "0.12" # Compile-time HTML templates255```256257- **Avoid `askama_axum` and similar integration crates that lag behind framework releases.** Instead, render templates manually and return `Html`:258 ```rust259 let html = template.render().map_err(|e| /* error handling */)?;260 Ok(Html(html))261 ```262 This avoids version coupling between the template engine and the web framework.263264### Asynchronous operation265266```toml267tokio = { version = "1.40", features = ["full"] } # Async runtime, timers, I/O, channels268```269270- `features = ["full"]` enables everything (runtime, macros, net, fs, time, sync). For libraries, enable only what you need: `["rt-multi-thread", "macros"]`.271- Prefer `tokio::spawn` for concurrent tasks, `tokio::select!` for racing futures.272- Use `tokio::sync::Mutex` (not `std::sync::Mutex`) when holding a lock across `.await` points.273274### System code with hashing and parallel execution275276```toml277blake3 = { version = "1.8", features = ["rayon"] } # Fast cryptographic hashing (SIMD-accelerated)278rayon = "1.10" # Data parallelism (parallel iterators)279memmap2 = "0.9" # Memory-mapped file I/O280```281282- `blake3` with `rayon` feature enables multi-threaded hashing of large files. Faster than SHA-256 for all input sizes.283- `rayon` turns `.iter()` into `.par_iter()` for trivial parallelism. Use for CPU-bound work over collections. Do not mix with `tokio` — rayon has its own thread pool.284- `memmap2` for zero-copy access to large files. Avoids reading entire files into memory.285286### WASM (WebAssembly)287288```toml289yew = { version = "0.21", features = ["csr"] } # Component framework (React-like)290patternfly-yew = "0.6" # PatternFly UI components for Yew291```292293- `yew` with `csr` (client-side rendering) for browser-targeted WASM applications.294- `patternfly-yew` provides pre-built UI components (tables, forms, navigation) following the PatternFly design system.295- Build with `trunk serve` for development, `trunk build --release` for production.296297### Serialization and deserialization298299```toml300serde = { version = "1", features = ["derive"] } # Serialization framework301serde_json = "1" # JSON302serde_yaml = "0.9" # YAML303toml = "0.8" # TOML (config files)304csv = "1.3" # CSV reading/writing305chrono = { version = "0.4", features = ["serde"] } # DateTime with serde support306```307308- Always enable `serde`'s `derive` feature. Use `#[derive(Serialize, Deserialize)]` on all data types that cross serialization boundaries.309- `chrono` with `serde` feature for serializable timestamps. Use `chrono::DateTime<Utc>` as the standard time type.310- For TOML config files, prefer `toml` crate over `serde_toml`.311312### Terminal / TUI applications313314```toml315ratatui = "0.29" # TUI framework (widgets, layout, rendering)316crossterm = "0.28" # Terminal manipulation backend317```318319- `ratatui` is the actively maintained fork of `tui-rs`. Provides widgets (tables, lists, charts, paragraphs) and a layout system.320- `crossterm` is the cross-platform terminal backend. Use with ratatui: `ratatui::prelude::CrosstermBackend`.321- Pattern: initialize terminal in `main()`, restore on exit (including panic). Use `std::panic::set_hook` to ensure terminal cleanup.322323### Git operations324325```toml326git2 = "0.19" # libgit2 bindings327```328329- `git2` provides full git operations (clone, commit, diff, log, blame) without shelling out to `git`.330- Requires `libgit2` (bundled by default via `libgit2-sys`). No system dependency needed.331- For simple operations (status, add, commit), shelling out to `git` via `std::process::Command` is simpler and avoids the compile-time cost of `git2`.332333## Crate Compatibility334335When using multiple crates that integrate with each other, verify version compatibility **before** writing application code:336337- **Integration crates** (e.g., `askama_axum`, `tower-http`, `sqlx` with runtime features) bridge two or more dependencies. All bridged versions must be compatible. Check the integration crate's `Cargo.toml` for its dependency version requirements.338- **Test compatibility early.** After adding a new integration crate, run `cargo check` on a minimal use before writing handlers or business logic. Discovering incompatibility after writing 500 lines of handler code wastes the entire batch.339- **When an integration crate lags behind its dependencies**, drop it and implement the glue manually. For example, if a template integration crate doesn't support the latest version of your web framework, render templates manually and wrap the output. A few lines of manual glue is better than pinning to an old framework version.340- **Pin integration crate versions explicitly** (e.g., `askama_axum = "=0.4.0"`) when you need a specific compatible combination, to prevent `cargo update` from breaking it.341342## Testing Strategies343344### Golden-value tests for numerical and domain code345346For code that computes numerical results (solvers, financial calculations, data transformations), compile-time correctness is necessary but not sufficient — the code can compile and produce wrong answers. Use golden-value tests:3473481. **Obtain reference values** from a known-good source (published tables, reference implementation, manual calculation)3492. **Create test fixtures** with input data and expected outputs3503. **Assert with tolerances** — use approximate comparison for floating-point results:351 ```rust352 assert!((result - expected).abs() < 1e-6, "expected {expected}, got {result}");353 ```3544. **Test edge cases explicitly** — zero inputs, boundary values, degenerate cases that are valid but extreme355356### Integration tests with fixtures357358For code that processes external data (HTML, files, API responses):3593601. **Store representative fixtures** in `tests/fixtures/` — real-world examples, not hand-crafted minimal inputs3612. **Test the public API end-to-end** — parse, transform, and verify the output in a single test3623. **Include malformed inputs** — test that bad data produces clear errors, not panics363364### Web application state365366Prefer simpler state patterns that avoid ownership complexity:367368- **Pass configuration (not connections) in web state.** For example, store a database path as a `String` and open a connection per request, rather than sharing `Arc<Mutex<Connection>>` across handlers. This eliminates lock contention and simplifies ownership.369- Use `Arc<T>` for truly shared read-only state (config, compiled templates, static data)370- Use per-request resources for anything with mutable state or cleanup requirements371372## Guidelines373374- **Batch over incremental.** The single most impactful practice is writing more code before compiling. Each compile cycle costs 10-30 seconds; eliminating 10 unnecessary cycles saves 2-5 minutes per task.375- **Read before writing.** Never modify a file you haven't read. The compiler errors from misunderstanding existing types cost more time than reading the file would have.376- **Fix root causes, not symptoms.** If the borrow checker rejects a pattern in 3 places, the API design is wrong — fix the signature, not the call sites.377- **Keep the dependency tree shallow.** Every new crate dependency adds compile time. Check if the standard library or an existing dependency already provides the functionality.378- **Use the type system, don't fight it.** If you're writing a lot of `.clone()`, `Rc`, or `unsafe`, step back and reconsider the data ownership model.379- **Verify crate APIs before committing to them.** The cost of discovering an API mismatch after writing 10 handlers is far higher than testing one minimal example first. This applies especially to template engines, integration crates, and anything with macro-based DSLs.380- **Domain bugs cost more than type bugs.** The compiler catches type errors, borrow errors, and lifetime issues. It cannot catch wrong formulas, incorrect sign conventions, or numerical edge cases. Invest verification effort proportional to the risk: domain-critical code needs golden-value tests, not just `cargo check`.381- **Split `lib.rs` from `main.rs` by default.** This is a one-time structural decision that enables integration testing, benchmarking, and reuse. Do it at project creation, not as a refactor later.382- **Respect the user's CLAUDE.md.** The user's global instructions override defaults. Check for project-specific conventions before applying generic Rust patterns.383384## Other385- Some AI LLMs suggest changing the Rust edition in Cargo.toml to 2021 from 2024. Their reasoning is mistaken: they are not up to date.The latest Rust edition is 2024 and Cargo.toml files with this should not be changed.