AI Code Review for Rust — What to Look For
Macroscope
Macroscope
Product

AI Code Review for Rust: What to Look For

AI code review for Rust: what the borrow checker cannot catch, why unsafe blocks and panics need review, and how AST-level analysis plus cross-file reasoning finds idiomatic Rust issues the compiler accepts.

AI code review for Rust occupies an unusual position. Rust's compiler already eliminates whole categories of bug that dominate review in other languages: use-after-free, data races across threads, null dereferences. If your mental model of code review is "catch the memory bug," Rust appears to have automated the reviewer away.

It has not. It has moved the work. What remains after the borrow checker is satisfied is a harder and more interesting review problem: code that compiles, is memory-safe, and is still wrong.

TL;DR — AI Code Review for Rust

  • The compiler is not the reviewer. cargo build passing means safe Rust is memory-safe, not correct; unsafe code can still violate memory-safety invariants.
  • The real Rust review surface is panic paths, error handling that swallows failure, unsafe invariants, and async cancellation.
  • Idiomatic Rust is a review concern, not a style concern: unwrap() in a library, clone() to escape a lifetime, and blocking calls in async contexts are all correctness-adjacent.
  • Macroscope ships a dedicated AST codewalker for Rust, one of ten languages with first-class parsing, enabling lower-latency reviews.
  • Cross-file reasoning matters most. Rust's module and trait system spreads a single behavior across many files, so diff-only review misses the interesting failures.
  • Custom rules let you encode crate-specific invariants with Check Run Agents scoped by path.

What Is AI Code Review for Rust?

AI code review for Rust is automated analysis of Rust pull requests that targets the defect classes the compiler permits: panics, error-handling mistakes, unsafe misuse, async pitfalls, and non-idiomatic constructs that will cause maintenance or performance problems later.

The distinction from other languages is what you can safely ignore. In Python or JavaScript, a large share of review effort goes to type confusion and null handling. In Rust the compiler owns those. That frees a reviewer — human or AI — to spend attention on logic, invariants, and the seams between modules.

What the Borrow Checker Cannot Catch

A useful way to scope Rust review is to enumerate what the compiler explicitly does not promise.

Panics

unwrap(), expect(), array indexing, integer division, and slice ranges all compile cleanly and abort at runtime. The compiler has no opinion about whether a panic is acceptable here. Context decides: unwrap() on a compile-time-known constant is fine; unwrap() on a parsed request field is an availability bug.

This is the single highest-value thing to review in Rust, and it is invisible to cargo build.

Error Handling That Silently Loses Information

Rust makes errors explicit but does not stop you discarding them. let _ = fallible(); compiles. So does mapping a rich error into a stringly-typed one, or collapsing five failure modes into a single variant. The result is a service that fails correctly and tells you nothing about why.

unsafe Invariants

unsafe does not disable the borrow checker; it lets you assert invariants the compiler cannot verify. Reviewing an unsafe block means checking the comment, or the absence of one: what invariant is being asserted, is it actually guaranteed, and does anything else in the module rely on it? This is precisely the kind of judgment that benefits from a reviewer that has read the whole module rather than the diff.

Async Cancellation and Blocking

async Rust has failure modes with no analogue in synchronous code. A future dropped at an await point stops mid-operation, so state can be left half-updated. Blocking I/O inside an async task starves the executor. Holding a std::sync::Mutex guard across an await may prevent the future from being spawned on a multithreaded executor when the guard is not Send; on runtimes where it compiles, lock contention while the guard is held can block or deadlock. These cases compile when the executor and guard type permit them.

Logic

The largest category, and the one no type system addresses. The if is inverted, the retry has no ceiling, the cache key is missing a field.

Why Idiomatic Rust Is a Correctness Concern

In many languages "idiomatic" is a taste argument. In Rust the idioms usually encode a real constraint:

  • clone() to escape a lifetime compiles and works, and quietly turns a borrow into an allocation in a hot path.
  • unwrap() in library code converts a caller's recoverable error into an unexpected panic, losing the normal Result error path.
  • Reimplementing an iterator chain as an indexed loop reintroduces bounds-check panics the iterator would have made impossible.
  • Vec<Box<dyn Trait>> where an enum would do trades a compile-time exhaustiveness check for a runtime one.

Macroscope's July 2026 code review improvements for Rust specifically targeted catching more idiomatic issues in Rust, on the reasoning above: in Rust, the idiom is frequently the invariant.

Why AST-Level Parsing Matters for Rust

Rust is a hard language to analyze with pattern matching alone. Macros expand into code that is not textually present. Traits mean a method call's behavior depends on a resolution that spans files. Generics mean the same source line has different semantics per instantiation.

Macroscope ships dedicated AST codewalkers for ten languages — Go, TypeScript, JavaScript, Python, Java, Kotlin, Swift, Rust, Ruby, and Vue.js — which enables lower-latency reviews than a purely agentic pass. All other languages are still fully supported through the agentic analysis engine. For Rust, the practical benefit of a real parse is that the reviewer resolves structure rather than guessing at it from text.

Cross-File Analysis Is the Whole Game in Rust

Rust's module system encourages small files, and its trait system distributes behavior across them. A single logical change often means: a trait definition here, an impl there, a caller somewhere else, and a re-export in lib.rs.

That means a reviewer confined to the diff is badly positioned. The interesting Rust bug is usually of the form "this impl no longer satisfies an assumption a distant caller makes." Macroscope's analysis reads beyond the changed lines for exactly this reason, and it is the same capability that produced 48% detection on a 118-bug benchmark across 8 languages.

Encoding Crate-Specific Rules

Generic review advice runs out quickly in a mature Rust codebase, because the rules that matter are local: never unwrap() outside main.rs and tests, all public errors must implement a specific trait, no std::sync primitives inside async modules.

Check Run Agents let you write these as markdown reviewers with include and exclude globs, so a rule that applies to your library crate does not fire on the CLI crate or the examples directory. Each agent runs as its own GitHub check, so a rule can be advisory or blocking independently.

Rust Review in CI and Locally

Two review surfaces are worth wiring up:

  • On the pull request, as GitHub checks, so review happens where the discussion happens.
  • Before you push, with the Macroscope CLI, which added a full-screen interactive review experience in July 2026 and supports excluding files from review. Catching a panic path locally is cheaper than catching it in review.

What AI Review Does Not Replace in Rust

Be clear about the division of labor:

  • cargo clippy catches a large set of lint-level idiom issues, and it is faster and cheaper than any AI reviewer. Run it. AI review is for what needs judgment or cross-file context, not for what a lint already flags.
  • The type system is your first reviewer. If an invariant can be expressed in a type, express it there instead of documenting it for a reviewer to check.
  • Tests remain the only thing that demonstrates behavior. A reviewer that reasons about your code is not evidence that your code works.

The best Rust review setup uses all four: the compiler for memory safety, Clippy for idioms with mechanical rules, AI review for logic and cross-file reasoning, and tests for behavior.

Frequently Asked Questions

Does AI code review work well for Rust?

Yes, though the value is in a different place than in dynamically typed languages. Because the compiler eliminates memory-safety and null bugs, AI review for Rust concentrates on panic paths, error handling, unsafe invariants, async pitfalls, and logic.

Does Macroscope support Rust?

Yes. Rust is one of ten languages with a dedicated AST codewalker (alongside Go, TypeScript, JavaScript, Python, Java, Kotlin, Swift, Ruby, and Vue.js), and Macroscope shipped code review pipeline improvements for Rust in July 2026 aimed at catching more idiomatic issues.

What can the Rust borrow checker not catch?

Panics from unwrap(), expect(), indexing and slicing; discarded or flattened errors; incorrect unsafe invariant assertions; async cancellation and executor-blocking problems; and all ordinary logic errors.

Should I use Clippy or AI code review for Rust?

Both. Clippy is faster and cheaper for lints with mechanical rules. AI review handles what needs cross-file context or judgment, such as whether an unwrap() is reachable from untrusted input.

Can I write custom Rust review rules?

Yes. Check Run Agents are markdown-defined reviewers with include/exclude path globs, so you can scope a rule to a specific crate or directory and run it as its own GitHub check.

Is unwrap() always a bug in Rust?

No, and a reviewer that flags every instance is noise. unwrap() on a value known at compile time is fine. It is a bug when the value derives from input, I/O, or configuration, where the correct behavior is to propagate the error.

Can AI review catch cross-file Rust bugs?

That is the main reason to use it. Rust distributes behavior across trait definitions, impls, and callers, so most consequential bugs are cross-file. Review limited to the diff will not see them.