· 6 min read ·

The Pain Has Moved: What Rust's Challenges Look Like in 2026

Source: rust

The Rust team published a survey retrospective this week examining where the language falls short from the perspective of its users. They went in expecting borrow checker complaints. They got those, voluminously. But the more telling finding is that the challenge landscape has matured alongside the language itself, and the bottlenecks developers face today are meaningfully different from the ones dominating the conversation five years ago.

That shift matters for how we think about Rust’s trajectory and what fixing the language actually requires.

The Borrow Checker Problem Is Partly Solved

The borrow checker is still a barrier, especially for newcomers. That will not change soon, and the Rust team knows it. But for developers who have spent serious time with the language, it is less often the active pain point than it used to be.

Non-lexical lifetimes, fully stabilized years ago, eliminated entire classes of false positives that used to make working code fail to compile. The error messages have improved dramatically over successive releases. The Polonius project, which reimplements the borrow checker’s core analysis using a more precise datalog-based approach, has been in development for years and will further reduce spurious rejections, particularly around cases where a borrow’s liveness does not actually overlap with a mutation.

Most experienced Rust developers describe the same arc: the borrow checker is brutal at first, then it becomes background noise. The type system tells you what the problem is; you fix it. What they describe as genuinely painful is not the learning phase. It is what comes after.

Async Rust Is Where the Real Friction Lives

If you have built anything non-trivial in async Rust, you know. The colored function problem, where async code can only be called from other async code and that requirement propagates through your entire call graph, is a language-level design constraint that no amount of tooling will fully abstract away. It is a real cost, and acknowledging it does not require thinking that Rust made the wrong trade-off.

But the depth of the issue goes further than that. Consider what was missing until relatively recently. Async functions in traits were not stable until Rust 1.75, released December 2023. Before that, you either used the async-trait crate, which boxes futures and adds a heap allocation per call, or you wrote desugared code by hand. Async closures did not stabilize until the 2024 edition, meaning entire patterns were unavailable or awkward for years.

async Drop, the ability to run async cleanup code when a value is dropped, still does not exist in stable Rust. This forces workarounds in any system where resource cleanup needs to happen asynchronously: database connection pools, socket cleanup, file flushes under certain runtimes. These patterns require manual solutions that the language should ideally handle.

The ecosystem fragmentation compounds this. Tokio has won in practice; it is the runtime most production Rust code uses. But the existence of multiple runtimes with historically incompatible abstractions cost the ecosystem years of duplicated effort. Libraries written against one runtime’s I/O types often could not interoperate with another. The situation has improved substantially, but the scars remain in the form of crates that quietly assume Tokio and crates that carefully stay runtime-agnostic at the cost of ergonomics.

Pin and Unpin, the machinery underpinning safe async programming, add cognitive overhead that has no real equivalent in other languages. Writing a hand-implemented Future requires understanding self-referential structs, why they cannot be moved in memory, and how Pin<P> projects that constraint through pointer types.

// When you need a custom Future, you eventually encounter this
impl Future for MyFuture {
    type Output = u32;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // self is Pin<&mut Self>, not &mut Self
        // projecting through Pin correctly requires unsafe or a proc-macro
        // most tutorials skip to "just use tokio::pin!" without explaining why
        todo!()
    }
}

The abstractions are well-designed for what they need to accomplish. They are also genuinely difficult, and the documentation gap between “here is the concept” and “here is how to apply it in a real codebase” remains wide.

Compile Times Remain a Genuine Cost

Rust compile times are slow for large codebases, and this is not just a developer experience complaint. It is a CI cost with a dollar figure attached. A team running builds on every pull request at $0.01 per CPU-second starts caring quickly about whether their full build takes 4 minutes or 14.

The compiler team has been working on this for years. The parallel frontend, enabling rustc to use multiple threads during earlier compilation stages rather than only during codegen, has been available experimentally and is approaching stable. The Cranelift backend for debug builds can substantially reduce compile times in development since it skips many LLVM optimization passes. sccache and the mold linker help at the tooling layer. Incremental compilation reduces rebuild times for iterative development.

But there is a ceiling. Rust’s type system does more work at compile time than most languages, and that work is not incidental. The monomorphization of generics, the borrow checking, the complex trait resolution, these are the price of the guarantees the language provides. There is a limit on how fast rustc can become without changing what the language statically verifies.

The Organizational Dimension

The survey findings, as the Rust team acknowledges, are more nuanced than a simple learning curve complaint. A significant part of that nuance is organizational rather than technical.

Adopting Rust means hiring developers who know it, or training developers who do not. Both options have real costs. The pool of experienced Rust developers is growing but remains small compared to languages with 20-plus year histories and massive enterprise adoption. Code review in Rust requires reviewers who can reason about lifetimes and trait bounds, which narrows who can give useful feedback on a PR. Onboarding a new team member onto a Rust codebase takes longer than onboarding them onto a Go or Python codebase.

These are not problems the language team can fix with better syntax or improved error messages. They require documentation, training materials, community investment, and time. The Rust Foundation has been investing here, but ecosystem-level change is slow by nature.

Go made different choices. It constrained the language deliberately to keep the learning curve shallow and the team onboarding cost low. Many organizations chose Go over Rust not because Go produces better software for their use case, but because the total organizational cost of Go is lower. That is a legitimate trade-off, and the Rust team acknowledging it in their survey analysis is more honest than pretending otherwise.

What Can Actually Improve

Some things are tractable. Polonius will reduce borrow checker false positives, removing friction for real cases that currently require workarounds or unsafe. Continued async stabilization, particularly async Drop and a cleaner story around Pin, will make the async half of the language feel less like a second-class citizen added after the fact. Compile time improvements, even marginal ones, compound over thousands of CI runs.

The deeper challenge is that Rust made deliberate choices to prioritize correctness and performance over ease of adoption. Those choices have been vindicated in every domain where Rust has succeeded: embedded systems, network infrastructure, cryptography, WebAssembly runtimes. But they create a real adoption tax that does not disappear just because the language matures.

The honest reading of the Rust team’s survey work is that they are trying to understand where that tax falls most heavily. Is it the initial learning curve, which is already well-documented? Is it the async ergonomics gap, which is actively being closed? Is it the organizational cost, which requires investment beyond the language itself? The answer is probably all three, in proportions that vary by organization and use case.

What the survey findings suggest, and what rings true from building things in Rust over the past several years, is that “steep learning curve” is a description of a symptom, not a diagnosis. Rust is hard in specific, identifiable ways, and those ways are changing as the language evolves. The team knowing that is a necessary precondition for improving it.

Was this interesting?