What’s New in Rust 1.96 for Developers

Rust 1.96.0 shipped on May 28, 2026, and solves one of the language's longest-standing ergonomics issues. The problem involved range types that could not implement Copy. This limitation frustrated many library authors over the years, and Rust 1.96.0 resolves it through the implementation of RFC 3550. Additionally, the release stabilizes two new assertion macros and introduces security improvements for Cargo users working with third-party registries.

·
Published May 29, 2026 · Updated May 29, 2026
·
⏱ 4 min read

Highlights

1 New core::range types (Range, RangeFrom, RangeInclusive) are now stable. They implement IntoIterator instead of Iterator, which means they can also implement Copy, something the original range types never could.
2 assert_matches! and debug_assert_matches! are now stable in the standard library. They work like assert!(matches!(..)) but print a Debug representation of the failing value, making test failures easier to diagnose.
3 WebAssembly targets no longer pass --allow-undefined to the linker by default. Undefined symbols are now a hard linker error rather than silently becoming Wasm imports from the "env" module.
4 Two Cargo security advisories ship with this release: CVE-2026-5223 (medium severity, tarball symlink extraction) and CVE-2026-5222 (low severity, URL normalization auth). Neither affects crates.io users, only third-party registry users.
5 The full migration to core::range types is not complete yet. Range syntax like 0..1 still produces the legacy std::ops types. The switch is expected to land in the Rust 2027 edition.
6 Cargo now allows a dependency to specify both a git repository and an alternate registry in the same entry. The git source is used locally; the registry version is used when publishing.

As a result, this release represents the culmination of years of planning. As previously reported on the Rust Blog, the non-copyable range types in std::ops represented a long-standing design conflict since the early days of Rust and were discussed extensively before the Rust 2024 Edition was finalized. Ultimately, the transition was deferred. RFC 3550 provides the resolution. The new copyable range types are implemented natively within core::range and are also re-exported through std::range for standard binary development, so either path works depending on the target environment. In summary, Rust 1.96.0 ships the library portion of RFC 3550. Several additional steps are still required before the language-level transition is fully completed for the 2027 Edition. For developers writing libraries today, the recommendation is clear: public APIs should use impl RangeBounds so they can accept both the old and new range types. When a concrete type is required, prefer the new core::range types.

Release Date May 28, 2026
Branched From Master April 10, 2026
Cargo CVEs Fixed CVE-2026-5223 (medium) and CVE-2026-5222 (low)
Edition Migration Target Rust 2027 (range syntax switch)

Copy Ranges: What RFC 3550 Actually Changes

The original range types in std::ops implement Iterator directly. Clippy has long flagged types that implement both Iterator and Copy as a potential footgun, because iterating a copied value rather than the original produces silent, confusing bugs. So the original ranges never got Copy. That meant you couldn't store a range inside a #[derive(Copy)] struct without manually splitting start and end into separate fields.

The new core::range::Range, core::range::RangeFrom, and core::range::RangeInclusive types solve this by implementing IntoIterator instead of Iterator. They convert into an iterator when you need one, but the range value itself stays Copy. Storing a slice accessor inside a Copy type is now straightforward, which matters a lot for compiler-adjacent code, parser combinators, and anything that passes ranges around by value repeatedly. If you work on systems tooling or write libraries that pass ranges around by value, this cleans up patterns you've probably been working around for a while.

The new RangeInclusive also makes its start and end fields public, which was avoided in the original because exposing the exhausted iterator state would have been messy. Since the new type separates iteration from the range itself, this is no longer a concern.

assert_matches! Lands After Years of Third-Party Workarounds

Anyone writing tests in Rust has probably reached for a third-party assert_matches! macro at some point. The pattern assert!(matches!(value, SomeVariant { .. })) works, but when it fails it prints nothing useful about what value actually contained. The new standard library macros fix that by printing the Debug representation of the failing value alongside the pattern, so test output gives you something to work with.

Worth noting: these macros are not added to the prelude. Third-party crates with the same name are common enough that doing so would have caused widespread breakage. You need to import them explicitly with use core::assert_matches; or use std::assert_matches;. That's a minor inconvenience, but it's the right call for compatibility. The reaction from the developer community has been warm, with several long-time Rust contributors describing it as overdue.


The full transition won't be completed until 2027 edition, when the meaning of the .. operator is expected to swap to the new type.


estebank, Rust compiler contributor, May 2026

WebAssembly Linker Behavior Change: What You Need to Check

This one can break existing Wasm builds silently upgraded to 1.96 if you haven't accounted for it. Previously, undefined symbols in WebAssembly targets were automatically converted to imports from the "env" module, which meant broken builds sometimes went undetected until runtime. As of 1.96, those undefined symbols are a hard linker error.

This was branched from master on April 10, 2026, and officially takes effect now with the stable 1.96.0 release. If your Wasm build relied on undefined symbols being converted to "env" imports intentionally, you have two options: set RUSTFLAGS=-Clink-arg=--allow-undefined to restore the old behavior, or annotate the relevant extern block in source with #[link(wasm_import_module = "env")] to make the intent explicit. If you maintain open source Rust tooling that targets Wasm, check your CI before shipping anything that depends on undefined symbol behavior.

Cargo Security Fixes and the New Dependency Git/Registry Feature

Two CVEs ship with 1.96. CVE-2026-5223 is a medium severity issue involving extraction of crate tarballs that contain symlinks, and CVE-2026-5222 is a low severity issue with authentication handling when URLs are normalized. Both only affect users of third-party registries. If you only use crates.io, you are not exposed to either vulnerability. Updating via rustup update stable is still the right move to stay current. If you want broader context on environment hardening, our recent Dirty Frag kernel CVE analysis highlights the critical importance of keeping both userspace development tools and the host kernel systematically patched in 2026.

On the new feature side, Cargo now lets a single dependency entry specify both a git repository and an alternate registry. The git version is used in local development; the registry version is used on publish. This is useful for teams that maintain forks or internal mirrors and want a cleaner workflow without switching dependency declarations between environments. For Linux DevOps pipelines, this significantly cuts down on manual coordination around patching and publishing.

Language and Compiler Additions Worth Knowing

A handful of smaller language changes also land in 1.96. The compiler now allows passing an expr metavariable to cfg, which tightens macro hygiene in complex procedural macro setups. Never types are now always coerced in tuple expressions, closing a long-standing edge case in type inference. The regression that blocked ManuallyDrop constants from being used as patterns, introduced back in 1.94.0, is also fixed here.

On the compiler side, LoongArch Linux targets gain link relaxation support, and the riscv64gc-unknown-fuchsia target baseline is updated to RVA22 plus vector extensions. Neither is mainstream for most Linux developers, but they matter for teams building toolchains or targeting embedded and RISC-V hardware. The minimum external LLVM version is now 21, so anyone building Rust from source should verify their LLVM version before upgrading. Teams tracking broader kernel and toolchain changes in 2026 should also follow the Linux 7.0 kernel feature changes, where Rust's role in the kernel is expanding alongside these compiler updates.

What Is Still Unresolved

The range type migration is the obvious open question. Range syntax like 0..1 still produces the old std::ops types. The plan is to switch to the new core::range types in the Rust 2027 edition, but edition-aware path resolution in the standard library does not exist yet. That infrastructure has to be built before the switchover happens. Library authors should design APIs around impl RangeBounds today to stay compatible through the transition.

There is also some open community discussion about whether the std::range::legacy::* naming will hold long term, and whether future improvements to range types will need another migration path. The Rust libs-api team's position, shared publicly, is that this specific design issue is unlikely to recur because the Copy-vs-Iterator conflict is the only substantive complaint the range types have had for close to a decade. That's a reasonable bet, but it's not a guarantee. The ongoing push to use Rust in the Linux kernel itself, covered in depth in our Linux kernel security and Rust piece, gives useful context on why the 2027 edition timeline matters beyond just language ergonomics.

LinuxTeck - A Complete Learning Blog

Tech News Stay updated with the latest Linux and open-source news, covering new releases, distro updates, security patches, and enterprise developments, delivered in plain language for sysadmins and developers.

About John Britto

John Britto Founder & Chief-Editor @LinuxTeck. A Computer Geek and Linux Intellectual having more than 20+ years of experience in Linux and Open Source technologies.

View all posts by John Britto →

Leave a Reply

Your email address will not be published.

L