Rust in Mid-2026: Copy Ranges, Native Async Traits, and What’s Still Coming

Rust 1.96 Just Shipped — Here’s What Changed

Rust 1.96.0 landed on May 28, 2026, and the headliner is something that’s been a quiet annoyance for years: range types can now be Copy.

The new core::range module introduces Range, RangeFrom, and RangeInclusive types that implement both Copy and IntoIterator. The old std::ops range types stay put for compatibility. If you’ve ever had to split a start and end field apart because you couldn’t store a range in a Copy struct, that workaround is gone.

assert_matches! and debug_assert_matches! are now stable

Two macros ship stable in 1.96. assert_matches! and debug_assert_matches! check that a value matches a pattern and panic with the value’s Debug output when it doesn’t — cleaner than the matches!() + assert!() dance most people were using.

let val = Some(42u32);
assert_matches!(val, Some(x) if x > 10);

debug_assert_matches! strips out in release builds, same as debug_assert!.

WebAssembly: undefined symbols now fail the build

WASM targets no longer accept undefined symbols by default. Previously the linker would silently produce a broken binary; now you get a hard error at link time. If you have a WASM crate that relied on the old permissive behavior, expect a build break — but those were already wrong builds.

Async Traits in 2026: What Works, What Doesn’t

Native async fn in traits stabilized in Rust 1.85, but plenty of codebases are still carrying the async-trait crate out of habit. For most use cases you don’t need it anymore.

For static dispatch, the pattern is straightforward:

trait Fetcher {
    async fn fetch(&self, url: &str) -> Result<String, Error>;
}

struct HttpFetcher;

impl Fetcher for HttpFetcher {
    async fn fetch(&self, url: &str) -> Result<String, Error> {
        // implementation
    }
}

No macro, no heap allocation, no extra dependency. The compiler desugars async fn in traits into return-position impl Trait under the hood.

The two cases where async-trait is still useful

Dynamic dispatch is the main one. Box<dyn Fetcher> where Fetcher has async methods won’t compile — async trait methods aren’t object-safe. If you need to pass trait objects around, you’re still stuck with the crate or nightly workarounds. The fix is being tracked but isn’t stable yet.

The other case is Send bounds. When you spawn tasks on Tokio, the returned future needs to be Send. Native async traits don’t automatically propagate that constraint; you have to annotate the trait definition explicitly. Not hard, but it will bite you on first contact.

What’s Still on the Way

Const traits

The ability to call trait methods in const contexts is a named project goal for 2026. The RFC is accepted and nightly has partial support, but stable isn’t there yet. It’s a blocker for heap operations in const contexts, and when it lands it will unlock a meaningful chunk of compile-time programming that’s currently off-limits.

Polonius (next-gen borrow checker)

Polonius is the replacement for the current NLL borrow checker. It fixes a class of false positives — cases where the compiler rejects valid code because it can’t prove borrows of disjoint struct fields don’t overlap. Progress came during the 2025H2 project goals period. Stable is the eventual target, not a near-term date.

Field projections and Pin ergonomics

A field_of! macro merged last half, enabling type-safe field access patterns. Pin ergonomics improvements are in the same bucket: making pinned self-referential types less painful without reaching for pin-project. Both are incremental steps, not finished features.

C++/Rust interoperability

The Rust project officially scoped out the C++/Rust interop problem space in 2025H2, with contractor support. Early stages — but it’s the first time interop has been a named project goal rather than just ecosystem-level tooling like cxx or autocxx.

Sources

Similar Posts