rust-code-review
SKILL.md
Rust Code Review
Review Workflow
Follow this sequence to avoid false positives and catch edition-specific issues:
- Check
Cargo.toml— Note the Rust edition (2018, 2021, 2024) and MSRV if set. This determines which patterns apply. Check workspace structure if present. - Check dependencies — Note key crates (thiserror vs anyhow, tokio features, serde features). These inform which patterns are expected.
- Scan changed files — Read full functions, not just diffs. Many Rust bugs hide in ownership flow across a function.
- Check each category — Work through the checklist below, loading references as needed.
- Verify before reporting — Load beagle-rust:review-verification-protocol before submitting findings.
Output Format
Report findings as:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
Quick Reference
| Issue Type | Reference |
|---|---|
| Ownership transfers, borrowing conflicts, lifetime issues | references/ownership-borrowing.md |
| Result/Option handling, thiserror, anyhow, error context | references/error-handling.md |
| Async pitfalls, Send/Sync bounds, runtime blocking | references/async-concurrency.md |
| Unsafe usage, clippy patterns, API design, performance | references/common-mistakes.md |
Review Checklist
Ownership and Borrowing
- No unnecessary
.clone()to silence the borrow checker (hiding design issues) - References have appropriate lifetimes (not overly broad
'staticwhen shorter lifetime works) -
&strpreferred overStringin function parameters when ownership isn't needed -
impl AsRef<T>orInto<T>used for flexible API parameters - No dangling references or use-after-move
- Interior mutability (
Cell,RefCell,Mutex) used only when shared mutation is genuinely needed - Small types (≤24 bytes) derive
Copyand are passed by value -
Cow<'_, T>used when ownership is ambiguous
Error Handling
-
Result<T, E>used for recoverable errors, notpanic!/unwrap/expect - Error types provide context (thiserror with
#[error("...")]or manualDisplay) -
?operator used with properFromimplementations or.map_err() -
unwrap()/expect()only in tests, examples, or provably-safe contexts - Error variants are specific enough to be actionable by callers
-
anyhowused in applications,thiserrorin libraries (or clear rationale for alternatives) -
_or_elsevariants used when fallbacks involve allocation (ok_or_else,unwrap_or_else) -
let-elseused for early returns on failure (let Ok(x) = expr else { return ... }) -
inspect_errused for error logging,map_errfor error transformation
Traits and Types
- Traits are minimal and cohesive (single responsibility)
-
derivemacros appropriate for the type (Clone,Debug,PartialEqused correctly) - Newtypes used to prevent primitive obsession (e.g.,
struct UserId(Uuid)not bareUuid) -
From/Intoimplementations are lossless and infallible;TryFromfor fallible conversions - Sealed traits used when external implementations shouldn't be allowed
- Default implementations provided where they make sense
-
Send + Syncbounds verified for types shared across threads
Unsafe Code
-
unsafeblocks have safety comments explaining invariants -
unsafeis minimal — only the truly unsafe operation is inside the block - Safety invariants are documented and upheld by surrounding safe code
- No undefined behavior (null pointer deref, data races, invalid memory access)
-
unsafetrait implementations justify why the contract is upheld
Naming and Style
- Types are
PascalCase, functions/methodssnake_case, constantsSCREAMING_SNAKE_CASE - Modules use
snake_case -
is_,has_,can_prefixes for boolean-returning methods - Builder pattern methods take and return
self(not&mut self) for chaining - Public items have doc comments (
///) -
#[must_use]on functions where ignoring the return value is likely a bug - Imports ordered: std → external crates → workspace → crate/super
-
#[expect(clippy::...)]preferred over#[allow(...)]for lint suppression
Performance
- No unnecessary allocations in hot paths (prefer
&stroverString,&[T]overVec<T>) -
collect()type is specified or inferable - Iterators preferred over indexed loops for collection transforms
-
Vec::with_capacity()used when size is known - No redundant
.to_string()/.to_owned()chains - No intermediate
.collect()when passing iterators directly works -
.sum()preferred over.fold()for summation - Static dispatch (
impl Trait) used over dynamic (dyn Trait) unless flexibility required
Linting
-
cargo clippy --all-targets --all-features -- -D warningspasses - Key lints respected:
redundant_clone,large_enum_variant,needless_collect - Workspace lint configuration in
Cargo.tomlfor consistent enforcement - Doc lints enabled for library crates (
missing_docs,broken_intra_doc_links)
Severity Calibration
Critical (Block Merge)
unsafecode with unsound invariants or undefined behavior- Use-after-free or dangling reference patterns
unwrap()on user input or external data in production code- Data races (concurrent mutation without synchronization)
- Memory leaks via circular
Arc<Mutex<...>>without weak references
Major (Should Fix)
- Errors returned without context (bare
return errequivalent) .clone()masking ownership design issues in hot paths- Missing
Send/Syncbounds on types used across threads panic!for recoverable errors in library code- Overly broad
'staticlifetimes hiding API design issues
Minor (Consider Fixing)
- Missing doc comments on public items
Stringparameter where&strorimpl AsRef<str>would work- Derive macros missing for types that should have them
- Unused feature flags in
Cargo.toml - Suboptimal iterator chains (multiple allocations where one suffices)
Informational (Note Only)
- Suggestions to introduce newtypes for domain modeling
- Refactoring ideas for trait design
- Performance optimizations without measured impact
- Suggestions to add
#[must_use]or#[non_exhaustive]
When to Load References
- Reviewing ownership transfers, borrows, or lifetimes → ownership-borrowing.md
- Reviewing Result/Option handling or error types → error-handling.md
- Reviewing async code, tokio usage, or Send/Sync bounds → async-concurrency.md
- General review (unsafe, performance, API design, clippy) → common-mistakes.md
Valid Patterns (Do NOT Flag)
These are acceptable Rust patterns — reporting them wastes developer time:
.clone()in tests — Clarity over performance in test codeunwrap()in tests and examples — Acceptable where panicking on failure is intentionalBox<dyn Error>in simple binaries — Not every application needs custom error typesStringfields in structs — Owned data in structs is correct;&strfields require lifetime parameters#[allow(dead_code)]during development — Common during iterationtodo!()/unimplemented!()in new code — Valid placeholder during active development.expect("reason")with clear message — Self-documenting and acceptable for invariantsuse super::*in test modules — Standard pattern for#[cfg(test)]modules- Type aliases for complex types —
type Result<T> = std::result::Result<T, MyError>is idiomatic impl Traitin return position — Zero-cost abstraction, standard pattern- Turbofish syntax —
collect::<Vec<_>>()is idiomatic when type inference needs help _prefix for intentionally unused variables — Compiler convention#[expect(clippy::...)]with justification — Self-cleaning lint suppressionArc::clone(&arc)— Explicit Arc cloning is idiomatic and recommendedstd::sync::Mutexfor short critical sections in async — Tokio docs recommend thisforloops over iterators — When early exit or side effects are needed
Context-Sensitive Rules
Only flag these issues when the specific conditions apply:
| Issue | Flag ONLY IF |
|---|---|
| Missing error context | Error crosses module boundary without context |
Unnecessary .clone() |
In hot path or repeated call, not test/setup code |
| Missing doc comments | Item is pub and not in a #[cfg(test)] module |
unwrap() usage |
In production code path, not test/example/provably-safe |
Missing Send + Sync |
Type is actually shared across thread/task boundaries |
| Overly broad lifetime | A shorter lifetime would work AND the API is public |
Missing #[must_use] |
Function returns a value that callers commonly ignore |
Stale #[allow] suppression |
Should be #[expect] for self-cleaning lint management |
Missing Copy derive |
Type is ≤24 bytes with all-Copy fields and used frequently |
Before Submitting Findings
Load and follow beagle-rust:review-verification-protocol before reporting any issue.
Weekly Installs
1
Repository
existential-birds/beagleGitHub Stars
40
First Seen
4 days ago
Security Audits
Installed on
mcpjam1
claude-code1
replit1
junie1
windsurf1
zencoder1