The conventional story about Rust is that it is hard to learn but worth the effort, and that once you understand ownership and borrowing, the difficult part is behind you. The Rust team’s new challenges survey complicates that narrative. Across developers ranging from novices to engineering managers running production systems, they found that the difficulty doesn’t go away with experience. It changes shape.
This is a more honest framing than the one Rust advocates typically offer. Beginners struggle with ownership, lifetime annotations, and the borrow checker’s sometimes-frustrating rejections of code that a developer can plainly see is safe. Experts struggle with something else: async’s parallel type system, thin certification infrastructure for regulated industries, an embedded ecosystem that remains uneven past the popular platforms. The challenges persist at every level; they are just different challenges.
The One Tax Everyone Pays
Across every cohort in the survey, regardless of experience level or domain, compilation time came up. The article includes a quote that breaks off mid-sentence: “Java takes about 100 milliseconds, Rust anywhere fr…” The truncation is almost fitting. A Java compilation baseline of roughly 100ms against Rust’s experience of tens of seconds for incremental builds and minutes for clean builds captures the gap without needing a conclusion.
The structural reasons for slow compilation are not incidental. Rust monomorphizes generics at every crate boundary, producing separate LLVM IR and machine code for each type instantiation. LLVM is a thorough optimizer, which is most of why Rust’s generated code performs well, but LLVM’s optimization pipeline is expensive. Release builds of larger production codebases, systems like TiKV or Substrate, can exceed 20 to 40 minutes on standard developer hardware. Clean builds of more modest projects routinely take 30 to 60 seconds.
The rustc-perf dashboard tracks compiler performance over time, and progress is real. Practical mitigations are available. Switching to the mold linker reduces link times by 8 to 10 times with a single line in .cargo/config.toml. sccache from Mozilla caches compilation artifacts and can cut CI build times by 60 to 80 percent on cache hits. The Cranelift backend skips LLVM entirely for debug builds; the Bevy game engine team reported debug build times dropping from 30 seconds to 10 seconds after switching. cargo check runs only frontend passes and provides feedback far faster than a full build, at the cost of not producing a binary.
# .cargo/config.toml — faster linking on Linux
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
These mitigations are optimizations within a structural constraint, not solutions to it. Go made compilation speed a first-class design goal from its earliest days, paying for it in expressiveness. Rust made the opposite choice, and production teams pay for it in build times. The 2023 Rust Annual Survey named compilation speed the top pain point for the fourth consecutive year, which suggests that neither the mitigations nor the incremental compiler improvements have meaningfully shifted the overall experience.
Async Rust as a Parallel Type System
The survey treats async complexity as a domain-specific problem for network developers, which is accurate but undersells the scope. Async Rust introduces a separate set of concerns layered on top of the ownership system: Pin<T>, Unpin, the distinction between Send and non-Send futures, runtime fragmentation, and cancellation safety. It functions in practice as a second language with its own failure modes.
async/await syntax landed in Rust 1.39 in November 2019. Async functions in traits stabilized in Rust 1.75 in December 2023, a gap of over four years. During that gap, every library author who wanted async trait methods depended on the async-trait proc macro, which allocated on the heap for every call. Async closures with proper capture semantics arrived in Rust 1.85 in February 2025. The stabilization timeline explains why so much async code still looks awkward; the primitives were not all available at the same time, and the ecosystem evolved around their absence.
The Send bound problem is representative of what makes async Rust hard for experienced developers. Multi-threaded executors like Tokio require spawned futures to be Send. A single non-Send type held across an .await point, anywhere in a call stack, breaks the entire future’s Send impl, and the compiler error points to the surface rather than the root cause.
// This won't compile with tokio::spawn.
// MutexGuard<State> is !Send, held across an .await point.
async fn do_work(state: Arc<Mutex<State>>) {
let guard = state.lock().unwrap();
some_async_operation().await; // future becomes !Send here
println!("{:?}", guard.value);
}
Tracking down a MutexGuard three abstraction layers deep that happens to be held across an await point is a tedious exercise in a large codebase, and the error messages in these situations are among the least informative in the language.
Cancellation is the deeper unresolved problem. Dropping a future cancels it silently, per the Future::poll contract. A future that acquires a lock and then yields at an .await can deadlock silently when cancelled. The type system provides no protection here. Tokio’s CancellationToken is opt-in and invisible to callers who don’t know to look for it. tokio-console and Loom help surface these issues, but they are not part of standard workflows. Bob Nystrom’s 2015 essay on the colored function problem describes the structural pressure: once any function is async, the color propagates through the entire call graph.
Runtime fragmentation compounds the difficulty. No async runtime is included in the standard library, a deliberate decision to support embedded and kernel contexts. Tokio dominates roughly 70 percent of async Rust projects, but async-std, smol, and Embassy serve different niches and don’t interoperate cleanly. Competing definitions of AsyncRead and AsyncWrite across the futures crate and Tokio reflect a coordination problem that predates stabilization and has no clean resolution in sight.
The Certification Gap in Regulated Industries
Safety-critical developers face a different category of problem. Rust’s memory safety properties are technically compelling for automotive, industrial, and aerospace applications, and the toolchain certification infrastructure is catching up, but unevenly.
Ferrocene, developed by Ferrous Systems and AdaCore and qualified by TÜV SÜD, achieved ISO 26262 ASIL D and IEC 61508 SIL 4 qualification in 2023, making it the first qualified Rust toolchain. The qualification covers the compiler as a software development tool under Part 8 of ISO 26262, not the generated code itself. Teams still need to validate their own code and processes. The qualification scope is deliberately narrow: specific compiler versions, specific target platforms, specific language feature subsets. Ferrocene tracks upstream rustc as a downstream fork, which means teams operating within its constraints work with a subset of the language.
MISRA published MISRA Rust in 2024, the first time MISRA has addressed any language other than C and C++. The Safety-Critical Rust Consortium, established by the Rust Foundation in 2024 with members including BMW, Arm, and Toyota Research Institute, is coordinating tooling and guidelines across the industry.
The Ferrocene Language Specification, donated to the Rust project after being produced for the qualification process, is the first formal language specification Rust has ever had. That it required a commercial certification effort to produce Rust’s first formal spec says something about how long the language operated without one.
DO-178C qualification for avionics has no clear path yet. Formal verification tools like Kani from Amazon and Creusot are maturing but are not accepted by major certification bodies. Ada with SPARK carries decades of tooling and institutional trust that Rust cannot replicate on a short timeline, and the gap is most visible at the top of the safety-integrity hierarchy.
Embedded: Solid in the Center, Thin at the Edges
The embedded ecosystem is more optimistic overall, though the specifics depend heavily on which hardware a team targets. embedded-hal 1.0, released in early 2024, stabilized the core hardware abstraction layer traits after years on the 0.x series. Embassy provides an async embedded framework with a zero-allocation interrupt-driven executor running on STM32, nRF5x, RP2040, and ESP32. RTIC uses the type system to enforce safe shared resource access across interrupt priorities, catching data races at compile time in a way that C simply cannot.
The problems concentrate at the edges. HAL implementations for less common microcontrollers are often maintained by one or two people, sometimes abandoned mid-implementation. DMA transfers present a structural challenge: DMA bypasses the CPU, so Rust’s ownership model cannot directly enforce buffer immutability during a transfer. Embassy’s solution requires 'static lifetimes on DMA buffers, which is correct but forces static memory allocation patterns that teams may not want.
Documentation for async embedded patterns lags the synchronous equivalents considerably, which matters when a team is deciding whether to adopt an async model for firmware. Embassy does not yet offer semver stability guarantees, a real constraint for products with multi-year firmware maintenance windows. Vendor SDKs remain C-centric, and Rust support varies widely by vendor and chip family.
What the Survey Represents
The Rust team publishing a document that names these problems clearly is significant. The quote they chose as a frame is worth reading carefully: “If all the things laid out were done, I’d be a happy Rust programmer. If not, I’d still be a Rust programmer.” That is an engineering manager adopting Rust for performance, and it captures both the genuine friction and the reason teams absorb it anyway.
The conventional wisdom was that Rust is hard to learn and then fine. The more accurate version is that Rust has real ongoing costs that change as teams develop expertise, and the judgment is whether the safety and performance properties justify those costs for a given domain. For a lot of domains, they do. The survey represents a language past the point where softening the narrative serves anyone, and that is a better place to be than the alternative.