
Using Claude Code with Rust projects
Claude Code is actually really good at writing Rust code! The latest models, like sonnet-4
, OpenAI’s o3
, and Google’s gemini-2.5-pro
in an agentic configuration have been suprisingly effective at working in even the most esoteric and punishing Rust codebases I work within.
For a guide on how to setup a working structure for Claude Code to work well with Rust projects, skip down below.
My experience
To give some context, the primary Rust codebase I’m working on at the time of writing has the following features:
- 10+ crates in a shared Cargo workspace
- A fullstack web app built in Dioxus (a pre-1.0, very unstable API React-like web framework in pure Rust)
- Oh, and there is also Bevy code in that app (a pre-1.0, fast evolving API ECS game-engine framework, in pure Rust)
- Using a custom crate I built for interoperating between the two
- Because it is fullstack, it needs to maintain certain dependencies behind feature flags, to make sure things that can’t compile to WASM / shouldn’t be in the client are separated from that build
- The app involves some controlling of robots via WebSerial, so one of the crates does that. That one is basically 100% vibe-coded, again web code in pure Rust
This is basically the worst case scenario you can think of if you are in the “LLMs just regurgitate training data!” camp. None of the core frameworks I’m building on top of are stable, or well documented (you ain’t gunna find much in that Stack Overflow dataset about them). Even the sparse documentation that exists for them online are completely out of date, like we’re talking entire APIs that don’t exist anymore from version 0.15 6 months ago to 0.16 today. On top of this, we’re working in Rust, a notoriously difficult language to learn with concepts uncommon in other languages (borrow checking, lifetimes, etc).
With a little bit of existing context, though, Claude Code and other agentic coding solutions work beautifully in this codebase. I am constantly kicking myself for working on features for hours in a day manually instead of trusting Claude Code to do it instead. Whenever I start getting tired at the end of the day, and I’m not as far along on something as I wish I was, I say “what the hell, I’ll see if Claude Code can do it,” basically certain it’ll fail and I’ll just throw away what it did.
And then it does the whole thing in 5 minutes…
Rust is actually ideal
It makes sense when you think about it. The Rust team has invested a lot of work into making the compiler extremely expressive, to the point it often tells you exactly why the code isn’t compiling. Plus, borrow checking and the type system are so strict, often you can rely on “if you get it to compile, it’ll work.” The complexity of Rust for humans actually makes it an ideal language for agents.
I recently got more serious about changing my workflow drastically to take full advantage of an AI-agent-first workflow, where the only time I manually code is when I’m in that “coding-to-discover” mode where I’m not entirely sure how I want something built yet. Otherwise, Claude Code gets the first crack at it.
We work in Rust, though, and that comes with some complications when it comes to these agent workflows when you want to expand into running agents in parallel. I like using OpenAI’s web version of Codex for well-defined tasks, especially if I think of them when out and about and I can just pull out my phone. Whenever jobs fail, it’s almost always because the initial setup time teeters around the maximum 15 minute limit, due to compilation time.
For Codex and similar hosted solutions, we’ll have to wait for cached environment support (Devin has this, but I don’t really like Devin’s setup). For locally run Claude code, though, we can do some stuff.
How to setup parallel Claude Code for Rust
The following is a guide for how to setup a working environment optimized for Claude Code to work on parallel feature branches on a Rust codebase. You can see an example project in this repository: Git Repository
The file structure
For parallel agents, you want to be using git worktree
(https://git-scm.com/docs/git-worktree). These are git branches that get checked out as folders in another directory, instead of replacing the existing working directory, and multiple of these working directory branches can exist at the same time, but share the same .git
context.
To optimize this for Claude Code, I recommend adopting this kind of folder structure:
Before
my-project/
.git/
Cargo.toml
...
After
my-project-workspace/
.cargo/
config.toml # the workspace Cargo config, I'll show you what's in here below
CLAUDE.md # the "workspace" claude code instructions, I'll show you what's in here below
my-project/
.git/
Cargo.toml
CLAUDE.md # the project claude code instructions
...
This changes things a little so now our “main” git repo is one layer deeper, in a “workspace” folder (call it whatever you want). You’ll end up running Claude Code from the workspace directory rather than the project directory. This will allow Claude Code to manage worktrees itself, checking them out as separate folders in the workspace directory!
Here’s what I mean.
CLAUDE.md (workspace)
In that “workspace” CLAUDE.md
, which gets loaded automatically when you run the claude
command, you’ll have something like this:
## Working Directory Structure
- `my-project/` - the main git working directory. You *DO NOT* do your work here. You create git worktrees from here.
- `<task name>/` - worktree directories for each task branch.
- `<task name>/.task.md` - where you store information about the task and the status. this is gitignored.
## Working on tasks
- When you are given a task, if a task branch name is not specified, create a git worktree from the main git working directory, and do your work in that worktree. Use the `-b` flag on the `git worktree add` command to create the branch.
- Before starting work, read the CLAUDE.md in the worktree (if not already read) for more context and instructions.
- When you finish the task, push the branch and create a PR using the `gh` tool.
- Name the branches with the prefix `claude`, ie. `claude/some-new-feature`.
- When the user asks you to cleanup a task, remove the worktree and delete the local branch for that task.
- As you finish work, add information about your plan, work done, etc. to the task file in the worktree directory.
- If continuing a task that has work in it already, read the `.task.md` in the task worktree directory to pick up context on work done.
- If you are given external references to read, files or URLs, read them before starting the task.
This creates a workflow where we can run claude
in the workspace directory, and give it a task, and it manages the worktrees for us! In addition, it loads the CLAUDE.md
for the project before starting to make sure it has that context, in addition to this workspace instruction set.
It also maintains a .task.md
file (which I added to my .gitignore
) where it keeps context about work done. This is something I threw in there to make it easy to restart tasks later without having to “resume” the Claude Code context explicitly, you should feel free to mess with how this works to find what you like best and works for you.
CLAUDE.md (project)
For the project-specific CLAUDE.md
, it’s very dependent on your project. I recommend having the following in those instructions:
- A folder structure for the project, that has a one sentence explanation of what each crate (or module) is responsible for
- Context one what technologies are used (Postgres vs mysql, etc.)
- Specific instructions you pick up over time for your preferences (“don’t use
query!()
insqlx
, usequery_as()
instead” is one I have) - Instructions for how to check its work to ensure it did everything right, and maintain style (“run
cargo check --all-features
andcargo fmt
after finishing”, “runcargo test
”). This one is important, this is part of why agentic coding does way better than one-shot coding: allowing it to iterate based on programatic feedback
.cargo/config.toml
This is how we control for compilation times while running multiple working directories:
# .cargo/config.toml
[build]
target-dir = "my-project/target"
This cargo config will be picked up by the project folders within the workspace, and tell each of the worktree working directories to use the same target/
folder as the “main” project. This means we only maintain one set of build dependencies / artifacts, even if we run 10+ parallel agents! It also means compilation doesn’t start from scratch, it’s always iterative compilation.
How to use it
- Run
claude
in the workspace directory - ”Add a view that lists all the todos in the database with nice styling”
- Claude should create a new worktree with an appopriate branch name, or start working from the existing one if this is a continuation
- When you’re happy with the code, you can tell Claude “push this to github and create a PR”, and it should use the
gh
tool to create a PR for you - If changes need to be made, you can resume the task and ask for the changes and for them to be pushed
- When the PR is merged, run
claude
and ask it to clean up the task, it’ll take care of the worktree for you!
Last Tip: Don’t Look!
I think the biggest mistake people are making when I meet them and they tell me “I don’t get how people get things done with agents, I run it and it does stupid stuff.” If you watch a coding agent today code, you are going to see it do some illogical stuff. For example, in a recent run I observed, I had it do a task that involved adding a dependency, and I could tell right away when it added it to the Cargo.toml
it was the wrong version and it would fail to compile.
I let it run, though, and when I came back, it had figured out it’s error (after writing the whole thing with the wrong / incompatible dependency, mind you), upgraded the crate, and re-wrote it all with the correct version in less than a minute, confirmed it all worked with cargo check
and it was done!
The point is, you let the agent run long enough with verification loop (compilation / tests / etc), it’ll often figure it out eventually. For you, it shouldn’t matter that it gets there in a roundabout / silly way, the only thing we care about is the resulting code and whether that works / is good.
So my advice: stop looking at it work! Let the agent do it’s thing, check in periodically to see if it’s asking for permission to do something (or enable notifications), be liberal about always-allowing commands that are non-destructive and enable auto-edit (it’s a git repo, what could go wrong really? just revert). Otherwise, let it run its course, you’ll be happier for it, and you’ll stop judging the agent on human standards of how it gets to its result.
It’s not about the journey, it’s about the destination.