The conventional story about Rust goes like this: the learning curve is brutal, ownership and borrowing will break your brain for a few months, and then you emerge on the other side as a confident, productive Rust developer. The Rust team’s new challenges report complicates that narrative significantly.
After interviewing developers across experience levels and domains, what the team found was not a simple difficulty curve that flattens with time. They found that challenges transform. Novice friction gives way to expert friction. The nature of the problem changes, but the friction itself persists. And underneath all of it, spanning every cohort, compilation performance sits as a universal productivity tax that no amount of expertise makes disappear.
This reframing matters for how teams plan Rust adoption. If you’re an engineering manager pitching a Rust migration to leadership, “it gets easier after six months” is only partly true, and only for some of the problems you’ll encounter.
The One Constant: Waiting for the Compiler
Every single cohort the team interviewed cited compilation times as a significant barrier. Not just beginners frustrated by slow feedback loops. Experts, architects, technical leads. Everyone. One developer put it directly: Java builds in about 100 milliseconds; Rust takes much longer. At any meaningful project scale, that gap compounds.
The reasons for this are structural. Rust performs monomorphization, generating specialized machine code for every concrete instantiation of a generic function. It relies on LLVM as its primary codegen backend, which is extremely capable but not fast. And the borrow checker’s analysis is non-trivial even before you get to code generation. The result is that a cold build of a moderately large Rust project can take minutes, and while incremental builds are faster, they are not as reliable as they are in some other ecosystems.
The toolchain has been making progress. The Cranelift codegen backend, which targets faster compilation rather than maximum runtime performance, is now available as a development-time alternative:
# .cargo/config.toml
[unstable]
codegen-backend = true
[profile.dev]
codegen-backend = "cranelift"
This can cut development build times substantially, trading some optimization for iteration speed. There is also cargo check, which skips code generation entirely and only runs the type checker and borrow checker, and cargo-nextest for faster test execution. But these are workarounds and optimizations around a structural constraint, not solutions to it. The Rust team acknowledges this. Improving compilation throughput is one of the most requested areas in every annual survey, and meaningful architectural work is ongoing, but it is a hard problem.
The Beginner Trap Everyone Already Knows About
Ownership, borrowing, and lifetimes remain the primary barrier for developers new to Rust. This is the part of the story that has been told many times. The mental model required to satisfy the borrow checker is genuinely different from what most programmers have built up through years of garbage-collected or reference-counted languages.
What the report reinforces is that this friction is real and persistent. Improved error messages, the borrow checker’s continued evolution, and resources like The Rust Book and Rustlings have all helped. But the conceptual leap is not going away. Languages are not going to simplify memory safety to the point where Rust’s model becomes obvious. The question is whether the tooling and educational resources can make the model learnable faster.
What Async Looks Like When You’re Deep In It
For network developers who advance past the fundamentals, async Rust is where a new category of complexity begins. This is a well-documented tension in the ecosystem, and the report gives it weight by confirming it appears consistently across experienced practitioners.
The core model for async in Rust is based on futures and the Poll trait. At the surface, writing async fn and await looks straightforward. The complexity surfaces when you start working with the trait system, combinators, or anything that requires understanding what the compiler generates. For a long time, async fn could not appear in traits at all without workarounds involving async-trait, which uses heap allocation under the hood:
// Before Rust 1.75, you needed the async-trait crate
#[async_trait]
trait Fetcher {
async fn fetch(&self, url: &str) -> Result<String, Error>;
}
// Stabilized in Rust 1.75 (December 2023)
trait Fetcher {
async fn fetch(&self, url: &str) -> Result<String, Error>;
}
The stabilization of async functions in traits in Rust 1.75 addressed one of the most complained-about gaps. But the full picture is more complicated. Return-position impl Trait in traits has limitations around object safety. The Pin type and its interaction with self-referential futures is genuinely difficult to reason about. And the runtime fragmentation between Tokio and async-std means that library compatibility is not guaranteed.
For teams building networked services at scale, this is not a beginner problem. It is a systems design problem that requires detailed understanding of how the async machinery works, and the documentation and tooling to support that understanding are still catching up.
Safety-Critical Domains Are Waiting on Certification Infrastructure
For developers working in safety-critical domains, automotive, medical devices, aerospace, the challenge is not expressiveness or ergonomics. It is certification. Standards like ISO 26262 (automotive functional safety), IEC 61508 (industrial safety), and DO-178C (aviation software) require qualified toolchains with documented processes, evidence, and support contracts.
Ferrocene, developed by Adacore and Ferrous Systems, made a significant step by becoming a qualified Rust toolchain for ISO 26262 and IEC 61508. This was a major milestone, and it is now commercially available. But qualification is expensive to maintain, lags the upstream compiler, and covers a narrower set of targets than the broader Rust toolchain. Teams working in these domains often cannot simply pick up the latest stable Rust release; they have to work within certified toolchain versions and accept the constraints that come with that.
This is a gap that the community cannot close through open source effort alone. It requires sustained commercial investment and coordination with standards bodies. The report naming this as a consistent pain point for safety-critical teams is useful because it clarifies that this is not a niche problem for a small set of users. There is real demand for Rust in certified environments, and the toolchain infrastructure to meet that demand is still being built.
Embedded Rust’s Maturity Plateau
Embedded developers face a different set of frustrations: ecosystem fragmentation and variable library maturity. The no_std environment, where you cannot assume an allocator or operating system, is well-supported by the core language. But the layers above that, hardware abstraction layers, peripheral access crates, BSPs for specific microcontrollers, vary significantly in quality and maintenance.
The Embedded Working Group has produced substantial infrastructure, and projects like Embassy have brought async Rust to embedded environments in a way that works well in practice. But a developer moving from one microcontroller family to another often finds that the library support is in a different state: complete and maintained for one target, partial for another, absent for a third.
This is partly a resourcing problem. Embedded Rust is a smaller community than web backend Rust, and maintaining hardware-specific libraries requires access to the hardware and ongoing effort as both the hardware and the compiler evolve. The report confirms that this uneven maturity is a consistent concern, not an occasional edge case.
What This Tells Us About Adoption Strategy
The most honest takeaway from the report is that Rust adoption planning should not rely on a simple time-to-productivity model. Different teams will hit different walls depending on what they’re building, and some of those walls are not going to come down on a predictable schedule.
For infrastructure and systems teams, the main friction is compilation throughput and async ergonomics. Both are improving. For safety-critical teams, the constraint is certification toolchain availability, which is a slower-moving problem. For embedded teams, the bottleneck is ecosystem coverage, which depends on community growth in specific hardware domains.
The quote from an engineering manager in the report captures something true: even with its friction, Rust remains the tool people choose, because the alternative, writing performance-sensitive or safety-critical systems in C or C++ without the guarantees Rust provides, carries its own costs. The challenges the report documents are real, but they are friction against a background of genuine value. The question for the Rust project is not whether to address them, but in what order and through what mechanisms.
The report is a step toward answering that. Naming domain-specific challenges alongside universal ones is more useful than the usual frame of “Rust is hard to learn.” It points to where investment, tooling, and ecosystem work will have the most leverage.