What's New in Rust in 2026: The Most Important Changes

Max WellsMax WellsFounder of Rustify

A comprehensive summary of the most important Rust releases in 2026: async closures (1.85), type alias impl Trait stabilization, Polonius borrow checker, and what's coming in Rust 2027 edition.

By Rustify Team: Updated March 2026

TL;DR: Rust 2026 is a landmark year: async closures (RFC 3668) stabilized in Rust 1.85 (February 2026), the Polonius borrow checker is in beta for opt-in testing, and the next Rust edition (2027) planning is underway. These changes resolve long-standing pain points in async Rust and open new ergonomic patterns.

  • Rust 1.85 (Feb 2026): Async closures: async || {} syntax, AsyncFn trait family
  • Rust 1.84 (Jan 2026): MSRV-aware dependency resolution in Cargo, unsafe extern blocks
  • Polonius: New borrow checker: more programs accepted, better error messages: in beta
  • Rust 2027 edition: Planning begun: expected to include NLL migration, async trait improvements
  • Stable: let chains, #[expect(lint)] attribute, float_next_up/down

Who Should Read This?

This article is for Rust developers who want to stay current with language changes that affect production code: not language enthusiasts tracking every RFC, but working engineers who need to understand which 2026 changes are worth adopting now and which are still experimental. Rust engineers in the United States earn $150K–$230K depending on seniority and domain, and staying current with language evolution is part of what distinguishes senior engineers who lead migrations from those who wait for the team to adopt new patterns. If you write async Rust, work in no_std environments, or maintain libraries that other teams depend on, the 2026 changes are directly relevant to your work.


What Is New in Rust 1.85 (February 2026)?

Rust 1.85's headline feature is async closures: async || {} now works correctly with lifetimes, enabling async callbacks and higher-order async functions that were previously impossible or required ugly workarounds.

Async closures were the last major ergonomic gap in async Rust. Before 1.85:

// Before Rust 1.85: this didn't work
fn register_callback<F: Fn() -> Future>(callback: F) { ... }
// Error: closures can't capture references and be async at the same time
 
// Workaround: Box<dyn Future>: heap allocation, no lifetime support
fn register_callback(callback: Box<dyn Fn() -> BoxFuture<'static, ()>>) { ... }

With Rust 1.85:

// Rust 1.85: async closures work naturally
use std::future::Future;
 
// The AsyncFn trait family: AsyncFn, AsyncFnMut, AsyncFnOnce
async fn process_items<F>(items: Vec<String>, callback: F)
where
    F: AsyncFn(&str) -> bool,
{
    for item in &items {
        if callback(item).await {
            println!("Processed: {}", item);
        }
    }
}
 
// Usage: async closure with captured reference
let prefix = "hello";
let result = process_items(
    vec!["hello world".to_string(), "goodbye".to_string()],
    async |s| s.starts_with(prefix)  // captures prefix by reference
).await;

This enables clean async higher-order functions: async iterators, middleware chains, and callback-based APIs are now idiomatic Rust rather than workaround code.

The practical impact is most visible in web frameworks and async middleware patterns. Before 1.85, writing a middleware chain where each step was an async closure required boxing every future and losing lifetime information. With AsyncFn, middleware chains are as ergonomic as their synchronous counterparts. Library authors can now expose APIs that accept async callbacks without forcing callers into heap allocation or 'static bounds.


What Is New in Rust 1.84 (January 2026)?

Rust 1.84 delivered MSRV-aware dependency resolution in Cargo: cargo add and cargo update now respect your declared minimum supported Rust version, preventing accidental upgrades to incompatible crate versions.

# Cargo.toml
[package]
name = "my-crate"
version = "0.1.0"
rust-version = "1.80"  # MSRV declared here
 
[dependencies]
serde = "1"  # Cargo will not auto-select versions requiring Rust > 1.80

Before 1.84, cargo update could upgrade a transitive dependency to a version requiring a newer Rust: silently breaking builds on older toolchains. MSRV-aware resolution prevents this.

Other 1.84 stabilizations:

FeatureDescription
unsafe extern blocksExplicit unsafe extern { fn foo(); }: requires unsafe to declare extern functions
#[expect(lint)] attributeLike #[allow] but warns if the lint doesn't fire: prevents stale suppressions
float_next_up() and float_next_down()IEEE 754 float neighbor functions
Extended const expressionsMore operations available in const contexts
NonZero unified APINonZero<u32>, NonZero<i64>: replacing NonZeroU32, NonZeroI64

The MSRV-aware resolution is particularly important for library authors and teams with heterogeneous deployment environments. Libraries that target a broad user base (often specifying MSRV 12–18 months behind current stable) previously had to manually audit every cargo update to verify no transitive dependency jumped to a version their MSRV cannot compile. Cargo 1.84 automates this check.


What Is the Polonius Borrow Checker?

Polonius is a new borrow checker algorithm that accepts more correct programs than the current NLL (Non-Lexical Lifetimes) borrow checker: it uses Datalog-based flow analysis instead of NLL's lexical approach.

The current borrow checker occasionally rejects correct programs: code that a human can verify is safe, but the borrow checker cannot prove. The classic example:

fn get_or_insert<'a>(map: &'a mut HashMap<String, String>, key: &str) -> &'a String {
    // This compiles in Polonius but fails in NLL (pre-Polonius)
    if let Some(value) = map.get(key) {
        return value;  // NLL thinks map is still borrowed here
    }
    map.insert(key.to_string(), "default".to_string());
    map.get(key).unwrap()
}

NLL's lexical analysis sees map.get(key) as holding a borrow through the whole if body: even when the returned reference is returned early. Polonius tracks exact data flow and correctly determines the borrow ends before map.insert.

Polonius status in 2026:

StatusDetail
Opt-in betaAvailable with -Z polonius=next in nightly
Breaking changesNone expected: Polonius is a strict superset of NLL
Stable timelineTargeted for Rust 1.87–1.89 based on current progress
Error messagesSignificantly better than NLL in many cases

Beyond accepting more programs, Polonius produces better error messages in cases where the borrow checker does reject code. NLL's error messages point to where the borrow was created and where the conflict occurs, but sometimes miss the most relevant location. Polonius can pinpoint the exact reason the borrow extends too far, making compiler errors more actionable.


What Rust Features Stabilized in 2025 (Recap)?

2025 was a productive stabilization year: let chains, gen blocks, precise captures in closures, and major const improvements all landed.

FeatureVersionDescription
let chains1.83if let Some(x) = foo && x > 0 { }: chain let bindings in conditions
gen blocks1.81gen { yield 1; yield 2; }: generator syntax for custom iterators
Precise closure captures1.82Closures capture only what they use, not the whole variable
const stabilizations1.83–1.85HashMap::new(), BTreeMap::new(), string operations in const
std::error::Error in core1.81Error trait moved to core: usable in no_std
#[diagnostic::on_unimplemented]1.78Custom compiler error messages for trait implementations
LazyCell and LazyLock1.80Standard lazy initialization without once_cell dependency

The std::error::Error in core change is particularly significant for embedded and no_std projects. Previously, implementing the Error trait required std: making it impossible in embedded contexts. Moving Error to core allows no_std libraries to implement and return typed errors using the same trait as std-dependent libraries, enabling better error handling patterns in embedded Rust.

The precise closure captures change (1.82) quietly resolves a common footgun. Before 1.82, a closure that accessed person.name would capture the entire person variable, preventing other code from accessing person.age simultaneously. With precise captures, the closure captures only person.name: allowing person.age to be used independently. This eliminates a category of spurious borrow checker errors in code using closures alongside structs.


What Is the Rust 2027 Edition Planning?

Rust editions happen every 3 years: the 2024 edition shipped in late 2024, and the 2027 edition planning has begun. Key proposed changes: migration to RPIT (return position impl Trait) in traits, async trait improvements, and ergonomic lifetime improvements.

Rust editions introduce opt-in changes that would otherwise be backwards-incompatible. The edition system lets Rust evolve without breaking existing code: cargo fix --edition automates most migration.

Proposed 2027 edition changes (under discussion):

ChangeDescriptionStatus
impl Trait in trait definitionsSimplify async traits: async fn in traits without boxingRFC in progress
Lifetime elision improvementsFewer explicit lifetime annotations in common patternsDiscussion
dyn Trait reference ergonomicsReduce verbosity with trait objectsEarly RFC
match ergonomics extensionFurther reduce boilerplate in pattern matchingDiscussion

No 2027 edition features are finalized yet: this represents the active planning discussion, not committed changes.

The 2024 edition (the most recent) focused on making existing patterns more ergonomic without changing semantics significantly. The migration tooling (cargo fix --edition) handled over 95% of changes automatically for most codebases, and the edition was widely adopted within six months of release: a sign that the migration cost was lower than developers anticipated. The 2027 edition discussion is more ambitious: the proposed impl Trait in trait definitions change, if stabilized, would allow library authors to write async fn in trait without requiring callers to box the returned future. This has been one of the most requested async Rust ergonomic improvements for several years.


How Do These Changes Affect Your Day-to-Day Rust Code?

For most working Rust engineers, the practical impact of 2026 changes is concentrated in three areas: async callback APIs, Cargo dependency management, and borrow checker patterns that previously required workarounds.

If you write async web services or async middleware:

  • Async closures (1.85) let you write middleware and callback-accepting functions without boxing futures. This simplifies API design for any library that takes async callbacks.
  • The AsyncFn trait family is the new preferred way to accept async functions as arguments: prefer it over Fn() -> impl Future<Output = T> for cleaner type bounds.

If you maintain a library with an MSRV policy:

  • Cargo 1.84's MSRV-aware resolution removes manual auditing from your cargo update workflow. Declare rust-version in Cargo.toml and Cargo enforces it during dependency resolution.

If you frequently encounter borrow checker rejections for valid code:

  • Polonius (available now on nightly with -Z polonius=next) may already accept programs your current stable borrow checker rejects. Testing on nightly gives you early access and lets you contribute feedback before stabilization.

Senior Rust engineers in the US who stay current with these changes are often the ones leading internal migration discussions: advocating for async closure adoption in middleware layers, updating Cargo MSRV policies, or piloting Polonius testing on nightly CI runs. This kind of language leadership is part of what differentiates $185K–$230K senior roles from mid-level positions.


What Common Mistakes Do Rust Developers Make When Adopting New Features?

The most common errors involve adopting unstable features in production code, misunderstanding the MSRV implications of new feature usage, and conflating async closures with regular closures returning futures.

  • Using nightly-only features in a library without clearly declaring it. Polonius and other features gated behind -Z flags are nightly-only. Libraries that use nightly features cannot be compiled on stable Rust: this is a hard dependency you are imposing on every user of your library. Unless your library is explicitly nightly-only (uncommon and usually undesirable), do not use nightly features in public library code.

  • Adopting async closures without updating the MSRV. Async closures (async || {}) require Rust 1.85. If your library or application declares a lower rust-version, using async closures will break builds for users on older toolchains. Update rust-version = "1.85" in Cargo.toml before using async closure syntax in published code.

  • Conflating AsyncFn with Fn() -> impl Future. The two are related but different. AsyncFn is a trait that captures the async closure's lifetime relationship between inputs and the returned future: enabling reference captures that Fn() -> impl Future<Output = T> cannot express. Using the older pattern for new code works but misses the ergonomic improvements in Rust 1.85.

  • Not testing with --all-features and --no-default-features after adding new const stabilizations. The const improvements in 1.83–1.85 enable new constructs in const contexts. If you add const usage gated behind a feature flag, CI must test all feature combinations to catch compilation failures in disabled feature paths.

  • Treating edition migration as an all-or-nothing decision. The cargo fix --edition migration tool handles most mechanical changes, but some edition changes require manual review: particularly around match ergonomics and closure capture semantics. Run cargo fix --edition on a branch, review every change, and test thoroughly before merging the edition bump to main.

  • Not subscribing to "This Week in Rust" for ongoing awareness. The Rust release cycle produces a new stable version every six weeks. Teams that do not track releases often discover stabilized features months after they land: continuing to use older workarounds unnecessarily. A fifteen-minute weekly review of the release notes prevents this.


Where Can You Get Structured Rust Training That Keeps Pace With the Language?

Rust's rapid evolution means self-study from year-old books leaves gaps. If you want a structured learning path that covers current-stable Rust: including async closures, const improvements, and the 2024 edition features: Rustify's 9-week bootcamp offers 1:1 coaching and project review from engineers who track Rust releases professionally. The program is updated with each stable release to reflect the current best practices.



Keep Reading

Frequently Asked Questions

Yes: async || {} closures are additive. Existing code using Box<dyn Fn() -> BoxFuture<'static, ()>> continues to compile. Async closures do not require boxing: they capture by reference efficiently when the closure's scope allows. You can migrate gradually: update new code to use async closure syntax and leave existing code unchanged. The two patterns can coexist in the same codebase without friction.

Based on the current roadmap, Polonius is expected to stabilize in the Rust 1.87–1.89 range (mid-to-late 2026). It is available now on nightly with -Z polonius=next for testing. Once stable, it becomes the default borrow checker: no code changes are needed to benefit. Programs that currently compile will continue to compile. Programs that currently fail due to NLL limitations may be accepted by Polonius, which is a purely additive improvement.

Async generators (write async gen { yield item; } to create an async iterator) are in active development in nightly. The gen block feature landed in 1.81 for synchronous generators; the async version is the next step. Expected stabilization: late 2026 or early 2027. The combination of async generators and async closures will make async iterator pipelines significantly more ergonomic than the current state.

Rust does not have a formal MSRV for the standard library: the standard library always requires the current stable version. The Cargo MSRV feature added in 1.84 applies to crate dependencies, not the standard library itself. If you need to support older Rust versions, the rust-version field in Cargo.toml enforces constraints on your dependency graph, but cannot prevent use of standard library APIs that did not exist in older versions.

The official sources: This Week in Rust (weekly newsletter), Rust release notes, and the Inside Rust Blog for team updates. The Rust Roadmap posts track in-progress features by team. For tracking specific RFCs, the rfcs repository on GitHub shows the full lifecycle from proposal through final comment period to stabilization.

Async closures and AsyncFn are most impactful for library authors designing callback-accepting APIs. MSRV-aware Cargo resolution is most impactful for library authors maintaining broad toolchain compatibility. Polonius benefits both: application developers writing complex borrow patterns get fewer false rejections, and library authors can expose richer lifetime-annotated APIs that Polonius can verify. Application developers get the most immediate benefit from let chains, precise closure captures, and const improvements, which improve day-to-day code clarity without requiring API design changes.


Sources

Ready to Land a $120k+ Rust Job in the US or Europe?