The conventional story about Rust goes like this: the borrow checker is hard, but once it clicks, everything gets easier. The Rust project’s new survey findings complicate that narrative in a useful way. Challenges don’t disappear as developers gain experience. They transform. Beginners fight ownership semantics. Experts fight async complexity, certification requirements, and ecosystem gaps that tooling cannot fix. The difficulty level stays roughly constant; the shape of it changes.
This is worth sitting with. Most languages have a steep beginner curve followed by a long plateau of productive fluency. Rust has a steep beginner curve followed by a series of domain-specific walls, each one specific enough that no single resource or community effort addresses all of them at once. That pattern reveals something true about what Rust is trying to do: it is not just a language with hard syntax, it is a language that makes previously-implicit constraints explicit, and there is always another layer of implicit constraints waiting to be made explicit.
The Compile Time Problem Is Not Going Away Quietly
Every cohort in the survey cited compilation speed, and the survey’s description of Java taking 100 milliseconds against Rust taking significantly longer maps to real experience. The root causes are structural. Rust relies on LLVM as its primary backend, and LLVM is thorough but slow. Generic monomorphization generates substantial amounts of code. The compiler frontend was single-threaded for years.
The community has been working on multiple fronts. The Cranelift codegen backend targets debug builds specifically, where you want fast iteration, not maximum optimization. Benchmarks have shown 2-3x improvements in debug build times. It is still experimental but progressing steadily. The parallel compiler frontend, exposed via -Z threads=N on nightly, is the other major effort; it addresses a different bottleneck and compounds nicely with Cranelift for development workflows.
For users today, the practical advice has not changed much: use lld or mold as your linker on Linux, keep crates reasonably granular to enable parallel compilation at the crate level, and lean on incremental compilation. None of this solves the fundamental issue, but compile times in a well-structured Rust project are meaningfully better than in a monolithic one. The structural fixes are coming; they just require sustained compiler engineering investment that cannot be rushed.
Async Rust Remains Genuinely Hard
Async complexity is where the gap between beginner and expert challenges becomes most visible. A beginner fighting the borrow checker is fighting something the language is actively trying to help them with: error messages have improved dramatically over the years, and the Polonius borrow checker redesign (in progress as of 2025) handles more programs correctly with fewer false rejections.
Async code involves a different category of difficulty. The Future trait desugars async fn into state machines with anonymous types. The Pin<P> abstraction exists because self-referential structs cannot be moved safely in Rust’s memory model, and futures can be self-referential at .await points. The consequence is that writing correct async code without understanding Pin and Unpin is possible until it suddenly is not, at which point the error messages can be deeply confusing.
The Send bound problem is particularly sharp for network developers. Whether a future is Send depends on whether every type held across an .await point implements Send. A single non-Send type buried three abstraction layers deep will cause the entire future to fail to implement Send, with an error message that points at the surface, not the cause. This is a known issue, not an obscure edge case.
// This compiles fine
async fn handle(conn: Arc<Mutex<Connection>>) {
let _guard = conn.lock().await; // MutexGuard held across await
}
// This does not compile with a spawn that requires Send
tokio::spawn(handle(conn)); // future is not Send
The async-fn-in-trait stabilization in Rust 1.75 addressed a long-standing ergonomics gap, but the Send bound interaction still requires workarounds in some cases. The async book is being developed separately from The Book precisely because async Rust is a substantial sub-discipline. The keyword generics initiative aims to let functions be generic over their async-ness, which would reduce API surface fragmentation, but it remains in early design.
The runtime fragmentation issue is structural. There is no standard async runtime in the language itself. Tokio is the de facto choice for most production network code, but the ecosystem split between runtime-specific and runtime-agnostic code adds friction when composing libraries. Progress on runtime-agnostic trait abstractions is real but incomplete.
Safety-Critical Certification Is a Real Gap, Not a Perception Problem
When safety-critical developers cite certification gaps, they are describing a concrete procurement and legal problem. Standards like DO-178C for avionics and ISO 26262 for automotive require qualified toolchains. The compiler itself must be shown to be correct, or at minimum, qualified to a defined level. C toolchains from IAR and Green Hills have decades of certification history.
Ferrocene, the collaboration between Adacore and Ferrous Systems, represents the most significant effort to address this. It achieved qualification for ISO 26262 (ASIL D) and IEC 61508 (SIL 4), which opened the door for Rust in automotive contexts. MISRA Rust provides coding guidelines analogous to MISRA C, giving safety teams a defined subset to target and audit against.
DO-178C qualification for avionics remains incomplete. These certification processes are expensive, slow, and require sustained commercial investment. The survey finding that safety-critical teams cite certification gaps is not a reflection of Rust’s technical suitability. Rust’s memory safety properties make it arguably more appropriate for these contexts than C. The gap is in the paper trail that regulators require, and closing it requires exactly the kind of sustained institutional effort that has been underway since Ferrocene’s formation.
Embedded: Ecosystem Maturity Is the Real Story
Embedded Rust has made substantial progress. The embedded-hal 1.0 release in early 2024 stabilized the hardware abstraction layer traits that driver crates depend on. The previous fragmentation between embedded-hal 0.2.x and 1.0 created a painful transition period where drivers and HALs were on incompatible versions; that period is resolving.
Embassy brought async to embedded development and is well-regarded for its design. It makes cooperative multitasking ergonomic on microcontrollers without an operating system. The tradeoff is that adopting Embassy is an architectural commitment. The async model adds mental overhead on top of already-complex embedded development, which explains why some embedded developers report higher, not lower, complexity after gaining Rust experience.
The gaps the survey likely captured are real: probe-rs has improved but debugging still lags behind established C workflows, LLVM coverage for exotic microcontroller architectures is inconsistent, and heap allocation in no_std contexts requires manual setup that C developers take for granted via malloc. These are solvable problems, but they require crate maintainers and tooling developers working on relatively narrow target audiences.
What the Challenge Migration Pattern Actually Means
The finding that challenges transform rather than diminish is not a criticism of Rust’s design. It is a consequence of Rust’s ambition. The language is trying to be simultaneously a systems language, an async network language, a safety-critical language, and an embedded language. Each of those domains has domain-specific requirements that a general-purpose language cannot fully satisfy out of the box.
The engineering manager quoted in the survey put it directly: even if none of the listed improvements ship, they would still be using Rust. That is the clearest signal that the challenge migration pattern is tolerable. Developers stay because the core value proposition, memory safety without garbage collection and a type system that encodes real invariants, delivers something that alternatives do not.
The compile time and async complexity problems are being addressed through concrete engineering work. The certification and ecosystem gaps require sustained investment that is harder to schedule but is also underway. The survey’s contribution is in naming which problems affect which populations, so that effort can be directed where it actually unblocks developers rather than where it is most visible from the language core.