Semantic diff for Rust: traits, impls and what a text diff misses
Rust makes text diffs particularly misleading: impl blocks move, trait bounds change meaning without changing shape, and macros hide the real edit. What a structural diff sees instead.
Rust code is unusually badly served by line-based diffs, because so much of its meaning lives in places a line diff cannot see. A trait bound tightens and one line changes, while every caller in the crate is now affected. An impl block moves between files and the diff reports a hundred lines of churn with no change in behaviour.
Four Rust-specific cases
| Change | What the text diff shows | What it means |
|---|---|---|
impl block moved to another module | Large delete and add | Nothing changed for callers, unless visibility changed with it |
| Trait bound added to a generic | One line | Every instantiation must now satisfy it |
| Lifetime elided or named | A few characters | Borrow relationships changed, and downstream code may stop compiling |
A function made pub(crate) | One word | The public API shrank |
What a structural diff reads
Parsing Rust with tree-sitter gives named nodes for functions, structs, enums, traits, impls and macro invocations. That is enough to identify a function by its owning impl and its signature rather than by its position, which is what makes moves and renames legible. It also makes the dependency question answerable: which functions call this one, which types implement this trait, what does the change reach.
There is a limit worth stating plainly. A syntax tree is not type inference. Tree-sitter knows a call is a call; it does not resolve which impl a method call selects. For questions that need real type resolution, the answer is rust-analyzer or the compiler, and any tool claiming otherwise is overselling. What a syntax-level index gives you is speed, tolerance of code that does not compile, and coverage across languages, which is the right trade for review and impact analysis.
Macros
Macro-generated code is the honest weak spot. A change inside a macro_rules! body can alter every expansion, and the expansions do not exist in the source tree. A structural diff will report the macro definition as changed, which is correct, and cannot enumerate the affected call sites the way it can for a plain function. Treat a macro edit as a wide change by default.
In practice
The Aura engine is written in Rust and indexes Rust the same way it indexes everything else: parse, hash each node, store the graph, compare. On a large crate the first index takes seconds and subsequent edits are incremental. The output is a change list by symbol, with the callers of each changed symbol, which is the shape a reviewer wants when a session has rewritten half a module.
See also: semantic diff compared with git diff and tree-sitter grammar packages for Rust and TypeScript.
What is a semantic diff for Rust?
A comparison that reads Rust structure — items, impl blocks, trait bounds, generics — instead of lines. It matters more in Rust than most languages because meaning lives in places a text diff renders as noise, such as a bound moving from a signature into a where clause.
Why is git diff misleading on Rust code?
Impl blocks move without changing behaviour, so a reorganised file reads as a rewrite. Trait bounds change meaning without changing much text. And macros expand into the code that actually runs, which the diff never shows you at all.
How do you diff Rust code by structure?
Parse both revisions with a Rust grammar, match items by path and signature rather than position, and compare the matched pairs. The result reports which functions and impls changed, and stays quiet about the ones that only moved.
Aura Crew vs Claude Code Loops: the autonomous work-loop, compared
Claude Code defines four loop types — turn-based, goal, time, and proactive. Aura Crew is the same idea productized: a dependency-ordered work-loop with proof, collision-safety, and any agent. Full comparison.
Autonomous coding agent loops, explained: turn-based, goal, time, and proactive
A coding agent loop is an agent repeating cycles of work until a stop condition is met. The four types explained — turn-based, goal-based, time-based, proactive — with when to use each and how to keep quality high.
How to run multiple coding agents in parallel without merge conflicts
Running several AI coding agents at once collides on shared files. The fix: a worktree per agent, a live team radar, and soft-to-hard zone claims so parallel agents never touch the same symbol. How Aura Crew does it.
Goal-based agent loops: giving a coding agent a definition of done
A goal-based loop keeps a coding agent iterating until a success criterion is met. The trick is a deterministic definition of done — tests, a score, a proof — not an LLM guessing at "good enough." How to write one.