Rust review
Walk the changed/specified Rust files and flag findings against the checklist below. Don't lecture on theory — for each finding, cite file:line and propose the concrete fix. End with a punch list ordered by severity.
Workflow
- Run the toolchain first. Before reading, run these in parallel and let their output drive the review:
cargo clippy --all-targets --all-features -- -W clippy::pedantic 2>&1 | head -200
cargo fmt -- --check (report formatting violations, do not auto-fix)
cargo check --all-targets 2>&1 | head -100
- Read the target files. If the user named files, just those. Otherwise diff against
main and review only what changed.
- Score findings as Major / Minor / Nit. Major = correctness, security, or public-API damage. Minor = idiom violations the reader will trip on. Nit = style.
- Propose fixes inline — don't just point at problems. If the fix is mechanical, offer to apply it.
Checklist
Ownership & borrowing
&String / &Vec<T> / &PathBuf parameters → should be &str / &[T] / &Path.
- Reflexive
.clone() to silence the borrow checker — flag and ask whether design change is better.
Rc<RefCell<T>> / Arc<Mutex<T>> used as the default ownership model → usually indicates the data flow fights ownership.
- Self-referential structs → recommend indices,
Rc, or redesign.
API surface
pub fn foo(...) -> Result<T, Box<dyn Error>> in library code — flag as Major. Public APIs need a typed error (see rust-error-design).
pub struct fields that should preserve invariants.
- Boolean parameters at call sites (
render(thing, true, false)) — recommend an enum.
- Returning
Vec<T> where impl Iterator<Item = T> would let callers skip allocation.
- Newtype opportunities: raw
String/u64 IDs passed around — flag domain primitives that deserve struct UserId(u64).
Errors (quick screen — defer deep refactors to rust-error-design)
Result<T, String> — Major. Stringly-typed errors lose structure.
.map_err(|e| format!("...: {:?}", e)) — Major. Discards the source error; breaks ? composition.
.unwrap() / .expect() outside tests without a justifying comment — Major in lib, Minor in bin.
let _ = result; or .ok(); swallowing errors — flag and ask why.
Logging & secrets
- Secrets in log statements. Grep for
debug!, info!, trace!, println!, eprintln! near identifiers containing token, secret, password, key, credential. Flag as Major.
println! / eprintln! mixed with tracing / log macros — pick one. Flag as Minor.
- Logging an error AND returning it (every layer doing this) — produces duplicate log lines. Errors should be enriched with
.context(...) and logged exactly once at the top.
- Glob imports of macro crates (
use tracing::*;) — Nit; prefer use tracing::{info, error, debug};.
Control flow
- Deeply nested
match (>3 levels) — usually collapsible with ?.
let mut result = false; + late-mutation pattern — extract a helper that returns Result<_, _> or Option<_>.
- Early
return from inside match arms after logging — flag as readability smell.
Tests
env::set_var / env::remove_var in #[test] without serialization — cargo test runs tests in parallel; env mutation races. Recommend serial_test crate or moving the test to a separate binary target.
.unwrap() in tests is OK; .expect("why") is better for debuggability.
- Tests asserting on error message strings (
assert!(e.to_string().contains("Failed to"))) — fragile. Prefer matching on error variants once the error type is typed.
Performance smells
format!("{}", x) where x.to_string() or pass-through would do.
Vec::push in a hot loop without Vec::with_capacity.
.collect::<Vec<_>>().iter() — allocated for nothing.
.to_owned() / .to_string() / .clone() on values only read.
Hygiene
/* */ empty doc-comment placeholders at file tops — delete.
- TODO/FIXME/XXX with no owner or date.
#[allow(...)] without a comment explaining why.
unsafe blocks without a // SAFETY: comment naming invariants — Major.
- Comments that explain WHAT (duplicating the code) instead of WHY.
- Misleading comments — e.g., "saturating" near
* rather than .saturating_mul().
Main / binary specific
fn main() -> Result<(), Box<dyn Error>> is fine for bin; not for lib.
- Retry/wait loops that exhaust attempts then
return Ok(()) — exit code should be non-zero on giving up.
- Silent fallback values (
"default".to_string() on error path) — surface the failure to the caller.
Report format
## Rust review — <scope>
### Major
- [`path.rs:L`] <finding> → <fix>
### Minor
- [`path.rs:L`] <finding> → <fix>
### Nits
- [`path.rs:L`] <finding>
### Clippy
<paste notable lints, omit duplicates of findings above>
### Suggested next step
<single most valuable action — e.g. "Refactor error types per `rust-error-design` before anything else; everything downstream depends on it.">
Keep the report skimmable. The user can ask you to apply any fix.
Source: outsideorbit/vaulpner — distributed by TomeVault.
1---2name: rust-review3description: Review Rust code for idiomatic style, anti-patterns, and common pitfalls — ownership, API design, logging, lints, secrets handling. Use when the user asks to review Rust code, judge whether something is idiomatic, refactor a `.rs` file, or audit a Rust module. Do NOT use for pure error-handling refactors (use rust-error-design) or async/tokio audits (use rust-async-audit) — invoke those instead when the focus is narrow. Use when this capability is needed.4---56# Rust review78Walk the changed/specified Rust files and flag findings against the checklist below. Don't lecture on theory — for each finding, cite `file:line` and propose the concrete fix. End with a punch list ordered by severity.910## Workflow11121. **Run the toolchain first.** Before reading, run these in parallel and let their output drive the review:13 - `cargo clippy --all-targets --all-features -- -W clippy::pedantic 2>&1 | head -200`14 - `cargo fmt -- --check` (report formatting violations, do not auto-fix)15 - `cargo check --all-targets 2>&1 | head -100`162. **Read the target files.** If the user named files, just those. Otherwise diff against `main` and review only what changed.173. **Score findings as Major / Minor / Nit.** Major = correctness, security, or public-API damage. Minor = idiom violations the reader will trip on. Nit = style.184. **Propose fixes inline** — don't just point at problems. If the fix is mechanical, offer to apply it.1920## Checklist2122### Ownership & borrowing23- `&String` / `&Vec<T>` / `&PathBuf` parameters → should be `&str` / `&[T]` / `&Path`.24- Reflexive `.clone()` to silence the borrow checker — flag and ask whether design change is better.25- `Rc<RefCell<T>>` / `Arc<Mutex<T>>` used as the default ownership model → usually indicates the data flow fights ownership.26- Self-referential structs → recommend indices, `Rc`, or redesign.2728### API surface29- `pub fn foo(...) -> Result<T, Box<dyn Error>>` in library code — flag as Major. Public APIs need a typed error (see `rust-error-design`).30- `pub` struct fields that should preserve invariants.31- Boolean parameters at call sites (`render(thing, true, false)`) — recommend an enum.32- Returning `Vec<T>` where `impl Iterator<Item = T>` would let callers skip allocation.33- Newtype opportunities: raw `String`/`u64` IDs passed around — flag domain primitives that deserve `struct UserId(u64)`.3435### Errors (quick screen — defer deep refactors to `rust-error-design`)36- `Result<T, String>` — Major. Stringly-typed errors lose structure.37- `.map_err(|e| format!("...: {:?}", e))` — Major. Discards the source error; breaks `?` composition.38- `.unwrap()` / `.expect()` outside tests without a justifying comment — Major in `lib`, Minor in `bin`.39- `let _ = result;` or `.ok();` swallowing errors — flag and ask why.4041### Logging & secrets42- **Secrets in log statements.** Grep for `debug!`, `info!`, `trace!`, `println!`, `eprintln!` near identifiers containing `token`, `secret`, `password`, `key`, `credential`. Flag as Major.43- `println!` / `eprintln!` mixed with `tracing` / `log` macros — pick one. Flag as Minor.44- Logging an error AND returning it (every layer doing this) — produces duplicate log lines. Errors should be enriched with `.context(...)` and logged exactly once at the top.45- Glob imports of macro crates (`use tracing::*;`) — Nit; prefer `use tracing::{info, error, debug};`.4647### Control flow48- Deeply nested `match` (>3 levels) — usually collapsible with `?`.49- `let mut result = false;` + late-mutation pattern — extract a helper that returns `Result<_, _>` or `Option<_>`.50- Early `return` from inside match arms after logging — flag as readability smell.5152### Tests53- `env::set_var` / `env::remove_var` in `#[test]` without serialization — `cargo test` runs tests in parallel; env mutation races. Recommend `serial_test` crate or moving the test to a separate binary target.54- `.unwrap()` in tests is OK; `.expect("why")` is better for debuggability.55- Tests asserting on error *message strings* (`assert!(e.to_string().contains("Failed to"))`) — fragile. Prefer matching on error variants once the error type is typed.5657### Performance smells58- `format!("{}", x)` where `x.to_string()` or pass-through would do.59- `Vec::push` in a hot loop without `Vec::with_capacity`.60- `.collect::<Vec<_>>().iter()` — allocated for nothing.61- `.to_owned()` / `.to_string()` / `.clone()` on values only read.6263### Hygiene64- `/* */` empty doc-comment placeholders at file tops — delete.65- TODO/FIXME/XXX with no owner or date.66- `#[allow(...)]` without a comment explaining why.67- `unsafe` blocks without a `// SAFETY:` comment naming invariants — Major.68- Comments that explain WHAT (duplicating the code) instead of WHY.69- Misleading comments — e.g., "saturating" near `*` rather than `.saturating_mul()`.7071### Main / binary specific72- `fn main() -> Result<(), Box<dyn Error>>` is fine for `bin`; not for `lib`.73- Retry/wait loops that exhaust attempts then `return Ok(())` — exit code should be non-zero on giving up.74- Silent fallback values (`"default".to_string()` on error path) — surface the failure to the caller.7576## Report format7778```79## Rust review — <scope>8081### Major82- [`path.rs:L`] <finding> → <fix>8384### Minor85- [`path.rs:L`] <finding> → <fix>8687### Nits88- [`path.rs:L`] <finding>8990### Clippy91<paste notable lints, omit duplicates of findings above>9293### Suggested next step94<single most valuable action — e.g. "Refactor error types per `rust-error-design` before anything else; everything downstream depends on it.">95```9697Keep the report skimmable. The user can ask you to apply any fix.9899---100> Source: [outsideorbit/vaulpner](https://github.com/outsideorbit/vaulpner) — distributed by [TomeVault](https://tomevault.io).101<!-- tomevault:4.0:skill_md:2026-05-23 -->