The Progress Bar That Won't Move

It is mid-afternoon, my coffee has gone cold, and I am staring at a terminal where cargo build --release has been running for almost six minutes. I changed exactly one line. I added a single field to a struct that the bot uses to track an arbitrage opportunity. One line, six minutes. The fan on my laptop has spun up to a sound my neighbors probably hear, and I am asking myself the same question every Rust developer asks at some point: did I really need to choose this language?

The Solana MEV bot I am building lives or dies by latency. Every microsecond matters when you are competing with other bots for the same arbitrage opportunity. That is the entire reason I picked Rust over the easier options. But the bargain is not free, and today I am paying the bill in build time. This piece is about that bill — what it actually costs, why it costs that much, and what I have learned about negotiating it down.

Why I Am Even In This Situation

Earlier, I wrote about why Python was not going to cut it for the hot path of an MEV bot — the global interpreter lock, the interpretation overhead, the latency floor that you simply cannot saw through with cleverness. The choice came down to Rust, and I made it the way most engineers do: I read the comparison posts, I nodded at the throughput charts, and I told myself the slow-build complaints were exaggerated.

They are not exaggerated. They are the central tax of writing production Rust, and the moment you start building anything non-trivial — async runtimes, serialization derives, Solana SDK dependencies, a half-dozen DEX integration crates — the tax compounds. The good news is that I am not alone. The bad news is that I am not alone.

How Common Is This Pain, Really

In September 2025, the Rust compiler performance working group published the results of a dedicated build-time survey. The headline number is the one that should sober anyone betting their stack on Rust: roughly 45% of former Rust users who responded said long compile times were among the reasons they stopped using the language, according to the official survey writeup. That is not a complaint about ergonomics or learning curves. That is people walking away.

The survey collected over 3,700 responses and reported an average satisfaction rating of 6 out of 10 with build performance. Roughly 55% of respondents said they wait more than ten seconds for incremental rebuilds — the small-edit, save-and-test loop that is supposed to be fast. About 42% had not tried any build performance improvement at all, which tells you that for most developers the suffering is just baked in.

The broader 2024 State of Rust survey, with 7,310 completed responses, put the same finding more bluntly: "Slow compilation was at the top of the list, as it seems to be a perennial concern of Rust users." The follow-on 2025 State of Rust survey published in March 2026 confirmed the pattern — resource usage and slow builds remain the loudest complaint, year after year.

In other words: my six-minute build is not a configuration mistake. It is a feature of the ecosystem.

What Six Minutes Actually Buys You

Before I gripe further, it is worth pausing on what the wait is for. Rust compiles slowly because it does an enormous amount of work that other languages either skip, defer to runtime, or push onto the human writing the code. The work is not arbitrary; it is the price tag attached to the runtime characteristics that pulled me here in the first place.

Think of it like the difference between a fast-food drive-thru and a steakhouse. Python's python script.py is the drive-thru — you pull up, you get your food, you go. There is no kitchen prep visible to you because there essentially is none. Rust's cargo build --release is the steakhouse — there is hours of mise en place happening behind the door before anything reaches your table, but the meal itself is something the drive-thru cannot match.

The payoff is concrete. Industry benchmarks consistently show Rust services handling significantly more requests per second than equivalent Go services, with much lower cold-start latency on serverless platforms. For an MEV bot that is racing every block leader on the network, that is not a nice-to-have. That is the entire reason the project exists.

But the steakhouse takes six minutes.

Why The Compiler Is Doing So Much

Four technical realities drive Rust's compile times. None of them is going away, because each one is the mechanical implementation of a feature Rust users actually want.

Monomorphization Is The Big One

When you write a generic function in Rust — say, a function that works on any Vec<T> — the compiler does not produce one piece of machine code that figures out the type at runtime. It stamps out a fresh, fully optimized copy for every concrete type you actually use. According to the official rustc developer guide, "Rust monomorphizes all generic types, meaning the compiler stamps out a different copy of the code of a generic function for each concrete type needed."

Use Vec<u64> and Vec<String> in the same program and you have two complete implementations to compile and optimize. Use a generic helper across forty different types in a serialization crate and you have forty. Languages that use type erasure — Java, Go — do not pay this cost. They generate one version of the code and figure out the types at runtime. Rust trades that runtime overhead for a compile-time avalanche, because the resulting machine code is as fast as if you had written each version by hand.

This is also why heavy use of serde derives can balloon build times in non-obvious ways. Each #[derive(Serialize, Deserialize)] expands into a substantial chunk of generated code per struct, and if your struct contains generics, the derives multiply along with them. The diagnostic tool cargo llvm-lines exists specifically to tell you which generic functions are dominating your intermediate representation output.

The Borrow Checker Pays Rent

Rust's memory safety guarantees are not magic. They are the result of an analysis pass that traces every reference, every lifetime, every function boundary in your code and proves that nothing can dangle, alias incorrectly, or be freed while still in use. This analysis runs across the entire codebase, and it is computationally expensive in a way that has no real shortcut. Either you check every reference or you do not have memory safety.

The Rust Performance Book notes that this analysis is one of the contributors to the front-end's wall-clock time, alongside type inference and macro expansion.

LLVM Earns Its Keep In Release Mode

Debug builds are uncomfortable. Release builds are the ones that hurt, and the reason is the LLVM optimization pipeline. In release mode, the compiler runs inlining, loop unrolling, dead code elimination, vectorization, and a long tail of other optimization passes whose entire purpose is to make your code run as fast as physically possible on the target CPU.

This is also why a single particularly nasty function can sometimes blow up your build to absurd lengths. There is a documented GitHub issue on the rust-lang repository titled "Very slow 80 minutes release build, due mir_pass_scalar_replacement_of_aggregates (sroa) optimization," describing a real project where a release build took over 80 minutes while the equivalent debug build finished in roughly 8. The optimization pass that was supposed to make the code faster instead got stuck doing its work on a particular function shape, and the project paid an hour-and-change tax for it.

The gap between debug and release is not subtle. One illustrative measurement reported on the BSWEN tutorial blog showed a debug build finishing in roughly 12 seconds while the equivalent release build of the same project took over four and a half minutes — about a twenty-two-fold gap. Numbers from a single tutorial are not gospel, but the directional point matches every Rust project I have ever shipped: release mode is where the wait gets serious.

Crates Are Compiled As Whole Units

The last reality is structural. Rust compiles at the crate level, not the file level. If you change one line in one file inside a crate, the compiler has to consider the entire crate. Incremental compilation — which I will talk about in a moment — softens this, but the underlying fact remains: a giant monolithic crate is a giant monolithic compile target. The PingCAP team documented this years ago in a blog post about scaling TiDB-related work in Rust, calling it the "huge compilation units" problem.

This is why workspace structure is not just code organization hygiene. It is a build-time strategy.

What Actually Helps

Reading complaints and explanations is one thing. Sitting in the chair while the laptop fan howls is another. Here is what has actually moved my own build times for the MEV project, and what the broader community reports working.

Cargo Check Is The Fastest Feedback Loop

For most edits, you do not need a runnable binary. You need to know whether the code type-checks and borrow-checks. cargo check does exactly that and skips code generation and the entire LLVM pipeline. The BSWEN tutorial measurement showed it running roughly six times faster than a full cargo build on their test project.

For my workflow, I keep a cargo check --workspace running in a watcher in one terminal pane while I edit in another. That tightens the inner feedback loop dramatically — you only pay the full build cost when you actually want to run the bot, not when you want to know if your latest refactor compiles.

Incremental Compilation Is Already Doing Work For You

Incremental compilation has been on by default for debug builds for years. The original Rust Blog post from 2016 describes the underlying mechanism: the compiler hashes the result of every internal query into a 128-bit fingerprint, and on the next build, it only redoes the work whose fingerprints have changed. The unit of caching is the Code Generation Unit, not the file.

The practical lesson is that the first build of the day always hurts and every build after it should hurt much less — provided you do not invalidate the cache by touching something low in the dependency tree. Edit a leaf file, the rebuild is fast. Edit a file in a foundational crate that everything depends on, and you are back to staring at the progress bar.

Splitting Into Workspace Crates

The single biggest structural change I have made on the bot is splitting what was originally one giant crate into a workspace of smaller ones — separating the on-chain client logic, the math libraries, the DEX integrations, and the executable entry point. The point is not aesthetic; it is that incremental rebuilds now operate at the granularity of the crate I actually changed. The illustrative measurement from the BSWEN tutorial showed a workspace-split project rebuilding roughly nine times faster on isolated single-crate changes than the original monolithic version.

For anyone building a non-trivial Rust application, the lesson is to start with a workspace from day one. Retrofitting later is doable but tedious — you discover all the assumptions you baked in about a single crate's internal visibility.

A Faster Linker

On Linux and macOS, the default system linker is often the bottleneck on the back end of the build. Switching to lld or mold is one of the most popular and easiest wins; the BSWEN tutorial reported a 27% improvement on a sample project after switching to lld. Alternative linkers were also one of the most commonly used optimizations reported in the Rust Compiler Performance Survey 2025.

sccache — But Not For Everything

A shared compilation cache like sccache can help dramatically on CI and on machines that do many clean builds. Vendor-aligned write-ups, including Earthly's blog post on sccache, describe extreme cases of dropping a 45-minute compile to roughly 5 minutes when caches are warm.

But you should treat that number as a marketing best case, not the median. An independent benchmark from the NeoSmart Files blog shows the messier reality: sccache can speed up test compilation modestly but can also slow down release builds — by up to 50% in their measurements — because the overhead of the cache lookup and the extra serialization is not always worth it. Their measured win on cargo nextest with a warm sccache was about 35% on standard 8-core CI runners, which is meaningful but a long way from "45 minutes to 5."

My own takeaway: sccache belongs in CI for tests and for first-time developer setup, not in the inner loop for release builds.

Docker Builds Need Their Own Strategy

If your deployment story involves Docker, the build time problem changes shape. Every container build is effectively a clean build unless you carefully cache layers. The well-known cargo-chef technique separates dependency installation from source compilation so that Docker can cache the dependency layer aggressively. A documented case study on the Nico's Engineering blog walked through reducing a project's Docker build from over 238 seconds to under 26 on subsequent builds — an 89% reduction — by combining cargo-chef with a slim base image.

The ratio matters more than the absolute numbers, because hardware varies. The structural lesson is that Docker plus Rust without cargo-chef is leaving real time on the table.

What's Coming From The Compiler Team

The Rust team is not pretending the problem does not exist. Two specific efforts are worth tracking.

The first is the Cranelift backend, which would replace LLVM for local development builds. The official Rust Project Goals page for 2025H2 describes the target as making Cranelift production-ready for Linux and macOS on x86_64 and aarch64. The current measured impact on large projects like Zed and Tauri is roughly a 20% reduction in code generation time, translating to about a 5% speedup on total clean builds. Experimental nightly results have shown 2-to-3x faster debug builds in some configurations. Cranelift is not aimed at release mode — LLVM stays for that — but for the inner loop of debug-and-iterate, the difference would be substantial.

The second is the parallel front end, described in the 2025H1 Project Goals. The goal is to parallelize the type checking, macro expansion, and HIR lowering phases — work currently done largely on a single core. The expected speedup once stabilized is in the 20-to-30% range. There are still issues being shaken out, including occasional deadlocks and reduced gains beyond 16 threads, but the trajectory is real.

On the macro level, the personal blog post on rustc performance shows the compiler getting roughly 1.77 times faster between versions 1.61 (May 2022) and 1.87 (May 2025) on a real benchmark project — nearly twice as fast over three years. That is steady progress. Not dramatic, but cumulative. And as noted in the May 2025 post on rustc speedups, most micro-optimizations have already been picked. Future gains will come from architectural changes like Cranelift and the parallel frontend, not from squeezing more out of the existing pipeline.

The other piece of context worth repeating from that post is the observation that, as it puts it, "Rust is not a company." The compiler team is largely volunteers with their own technical interests, and not all of them are particularly interested in performance work. The codebase is roughly 600,000 lines of Rust, with another similarly sized standard library, supporting 8 Tier 1 and 91 Tier 2 targets, with over 10,000 open issues and a merge rate of 25 to 30 PRs per day. Compiler performance has to compete for attention with everything else.

So What Do I Actually Do Tomorrow Morning

I keep building. The bot still needs to ship, and Rust is still the right call for the hot path. The wait is the wait. What the survey data and the contributor blog posts have done for me is reframe the wait from "my setup is broken" to "this is the cost of admission, and here is the menu of techniques to lower it."

My current rotation looks something like this. I keep cargo check running in a watcher for the moment-to-moment work. I have split the project into a workspace and I think hard before adding anything to a foundational crate that everything depends on. I use a faster linker locally. I keep sccache in my CI configuration but not in my local release-build path. And when I have to do a release build because I am about to deploy, I accept that I am going to lose six minutes of my life and I use the time to read another paper or stretch my back.

The deeper lesson is that performance is a system, not a setting. Rust spends compile-time so I do not have to spend runtime, and as long as I am building something where every microsecond on the wire matters, that is a trade I will keep making. I just want to be honest about what I am paying.

Key Takeaways

  • Rust's slow builds are not a bug or a misconfiguration — they are the structural cost of monomorphization, borrow checking, and aggressive LLVM optimization, all of which exist to deliver Rust's runtime characteristics.
  • The 2025 compiler performance survey published by the Rust team found that roughly 45% of former Rust users cited long compile times among their reasons for leaving, and 55% of current users wait more than ten seconds for incremental rebuilds.
  • High-leverage mitigations include using cargo check for the inner feedback loop, splitting code into workspace crates, switching to a faster linker, and applying cargo-chef for Docker pipelines.
  • sccache helps in CI and for tests but can actually slow down release builds in independent benchmarks — treat vendor numbers with caution.
  • The Cranelift backend and the parallel front end are the two structural improvements worth tracking; rustc is also gradually getting faster on its own, with measured progress of roughly 1.77x over three years of releases.

Disclaimer

This article is for informational and educational purposes only and does not constitute financial, investment, legal, or professional advice. Content is produced independently and supported by advertising revenue. While we strive for accuracy, this article may contain unintentional errors or outdated information. Readers should independently verify all facts and data before making decisions. Company names and trademarks are referenced for analysis purposes under fair use principles. Always consult qualified professionals before making financial or legal decisions.