--- name: rust-code-review description: "Rust Code review checklist and decision framework for Rust PRs, derived from rust-best-practices." globs: - "**/*.rs" alwaysApply: true --- # Rust Code Review Use this for Rust review work where consistency, safety, and maintainability matter. Follow ths standards in https://github.com/apollographql/rust-best-practices ## Mandatory pre-merge checks - Ownership and data flow are intentional. - Error handling is explicit and aligns with crate/binary boundaries. - Clippy and format quality are clean in touched files. - Performance changes are measured before acceptance. - Public APIs are documented; docs match runtime behavior. - Tests cover intended behavior and error paths. - Unsafe or raw-pointer usage is justified and constrained. ## Severity matrix - P0: unsafe memory bug, panic in recoverable production path, silent data corruption. - P1: correctness bug, missing error propagation, invalid API contract. - P2: likely performance regression, missing public API docs, flaky tests. - P3: style/readability issues, avoidable clone/allocation, unnecessary complexity. ## Review skills ### Ownership-first coding - Prefer borrowing (`&T`, `&mut T`) over cloning. - Use `Clone` only when ownership is required or snapshots are explicitly needed. - Treat unnecessary clones (especially in loops) as likely regressions. - Reject `clone` on `Copy` types. ### Value vs reference - Pass `Copy`/small POD types by value. - Pass large heap-backed or non-trivial objects by reference. - Surface ownership intent in function signatures. - Use `Cow<'_, T>` when input may be borrowed or owned. ### Fallible control flow - Use `let PATTERN = EXPR else { ... }` for expected early exits. - Use `if let ... else` when divergence needs additional logic. - Prefer `?` for bubbling errors. - Avoid `unwrap`/`expect` in production except when impossible-by-design cases are documented. ### Allocation and allocation timing - Prefer `_else` APIs to avoid eager allocation (`ok_or_else`, `map_or_else`, etc.). - Keep iterator chains lazy; allocate only when required by terminal ops. - Do not collect and allocate only to throw away data. ### Iterator vs loop - Use iterator chains for data transformation and composition. - Use `for` for early exits and side-effect-heavy or control-heavy loops. - Require readable formatting; avoid long unreadable chains. ### Lints and static checks - Run and fix warnings from: - `cargo clippy --all-targets --all-feature --locked -- -D warnings` - Do not globally silence useful lints. - Prefer `#[expect(clippy::...)]` with rationale instead of `#[allow(...)]` unless fully justified. ### Error discipline - Libraries: prefer typed errors (`thiserror` and `#[from]` conversions). - Binaries: `anyhow` acceptable, but keep context rich and actionable. - Test both success and error behavior. ### Tests as behavior docs - One behavior per test. - One core assertion per test where possible. - Names should be descriptive sentence-like statements. - Prefer unit tests for internals, integration tests for public behavior. - Use snapshot tests only for complex, stable structured outputs. ### Documentation and comments - Use `///`/`//!` for API behavior and constraints. - Use `//` for `why`, safety rationale, platform constraints, and assumptions. - Remove stale comments; prefer smaller functions over narrative comments. - Link TODOs to issues instead of leaving bare `TODO:`. ### Pointers and concurrency - Prefer `&`/`&mut` before any heap pointer. - Use `Arc` for cross-thread shared ownership; `Rc` for single-threaded. - Use `Box` for recursive/heap allocation needs. - Review raw pointer usage as unsafe boundaries with explicit invariants. ## Quick rejection triggers - Unnecessary clones in hot paths. - Unjustified `allow(clippy::...)`. - Silent recovery from `Err` that discards root cause. - Copying large types by value without a proof of intent. - Comments that simply restate what code already expresses. - TODOs without ownership/context.