|
| 1 | ++++ |
| 2 | +path = "2026/05/28/Rust-1.96.0" |
| 3 | +title = "Announcing Rust 1.96.0" |
| 4 | +authors = ["The Rust Release Team"] |
| 5 | +aliases = ["releases/1.96.0"] |
| 6 | + |
| 7 | +[extra] |
| 8 | +release = true |
| 9 | ++++ |
| 10 | + |
| 11 | +The Rust team is happy to announce a new version of Rust, 1.96.0. Rust is a programming language empowering everyone to build reliable and efficient software. |
| 12 | + |
| 13 | +If you have a previous version of Rust installed via `rustup`, you can get 1.96.0 with: |
| 14 | + |
| 15 | +```console |
| 16 | +$ rustup update stable |
| 17 | +``` |
| 18 | + |
| 19 | +If you don't have it already, you can [get `rustup`](https://www.rust-lang.org/install.html) from the appropriate page on our website, and check out the [detailed release notes for 1.96.0](https://doc.rust-lang.org/stable/releases.html#version-1960-2026-05-28). |
| 20 | + |
| 21 | +If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (`rustup default beta`) or the nightly channel (`rustup default nightly`). Please [report](https://github.com/rust-lang/rust/issues/new/choose) any bugs you might come across! |
| 22 | + |
| 23 | +## What's in 1.96.0 stable |
| 24 | + |
| 25 | +### New `Range*` types |
| 26 | + |
| 27 | +Many users expect `Range` and related `core::ops` types to be `Copy`, but this is not the case: they implement `Iterator` directly, and [it is a footgun to implement both `Iterator` and `Copy` on the same type](https://rust-lang.github.io/rust-clippy/rust-1.95.0/index.html#copy_iterator) so this has been avoided. [RFC3550] proposed a set of replacement range types that implement `IntoIterator` rather than `Iterator`, meaning they can also be `Copy`. The standard library portion of that RFC is now stable, introducing: |
| 28 | + |
| 29 | +- `core::range::Range` |
| 30 | +- `core::range::RangeFrom` |
| 31 | +- `core::range::RangeInclusive` |
| 32 | +- Associated iterators |
| 33 | + |
| 34 | +A Rust version in the near future will also add `core::range::RangeFull` and `core::range::RangeTo` as re-exports from `core::ops` (these do not implement `Iterator` and already implement `Copy`), and `core::range::legacy::*` as the new home for the current ranges. Range syntax like `0..1` still produces the legacy types for now, but will be updated to `core::range` types in a future edition. |
| 35 | + |
| 36 | +With these stabilizations, it is now possible to store slice accessors in `Copy` types without splitting `start` and `end`: |
| 37 | + |
| 38 | +```rust |
| 39 | +use core::range::Range; |
| 40 | + |
| 41 | +#[derive(Clone, Copy)] |
| 42 | +pub struct Span(Range<usize>); |
| 43 | + |
| 44 | +impl Span { |
| 45 | + pub fn of(self, s: &str) -> &str { |
| 46 | + &s[self.0] |
| 47 | + } |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +The new `RangeInclusive` also makes its fields public, unlike the legacy version which avoided exposing the exhausted iterator state. This isn't a concern with the new type since it must be converted to begin iteration. |
| 52 | + |
| 53 | +Library authors should consider making use of `impl RangeBounds` in public API, which accepts both legacy and new range types. If a concrete type is needed, prefer using new ranges as this will eventually become the default. |
| 54 | + |
| 55 | +[RFC3550]: https://rust-lang.github.io/rfcs/3550-new-range.html |
| 56 | + |
| 57 | +### Assert matching patterns |
| 58 | + |
| 59 | +The new macros `assert_matches!` and `debug_assert_matches!` check that a value matches a given pattern, panicking with a `Debug` representation of the value otherwise. These are essentially the same as `assert!(matches!(..))` and `debug_assert!(matches!(..))`, but the printed value improves the possibility of diagnosing the failure. |
| 60 | + |
| 61 | +These new macros have not been added to the standard prelude, because they would collide with popular third-party crates that provide macros with the same name. Instead, they should be manually imported from `core` or `std` before use. |
| 62 | + |
| 63 | +```rust |
| 64 | +use core::assert_matches; |
| 65 | + |
| 66 | +/// [Random Number](https://xkcd.com/221/) |
| 67 | +fn get_random_number() -> u32 { |
| 68 | + // chosen by a fair dice roll. |
| 69 | + // guaranteed to be random. |
| 70 | + 4 |
| 71 | +} |
| 72 | + |
| 73 | +fn main() { |
| 74 | + assert_matches!(get_random_number(), 1..=6); |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +### Changes to WebAssembly targets |
| 79 | + |
| 80 | +WebAssembly targets no longer pass `--allow-undefined` to the linker which means that undefined symbols when linking are now a linker error instead of being converted to WebAssembly imports from the `"env"` module. This change prevents modules from linking unless all linking-related symbols are defined to catch bugs earlier and prevent accidental issues with symbol naming or similar. |
| 81 | + |
| 82 | +Undefined linking-related symbols are often indicative of build-time related bugs or misconfiguration. If, however, the old behavior is intended then it can be re-enabled with `RUSTFLAGS=-Clink-arg=--allow-undefined` or by editing the source code and using `#[link(wasm_import_module = "env")]` on the block defining the symbol. |
| 83 | + |
| 84 | +This change was [previously announced](https://blog.rust-lang.org/2026/04/04/changes-to-webassembly-targets-and-handling-undefined-symbols/) on this blog, and now takes effect in Rust 1.96. |
| 85 | + |
| 86 | +### Stabilized APIs |
| 87 | + |
| 88 | +- [`assert_matches!`](https://doc.rust-lang.org/stable/std/macro.assert_matches.html) |
| 89 | +- [`debug_assert_matches!`](https://doc.rust-lang.org/stable/std/macro.debug_assert_matches.html) |
| 90 | +- [`From<T> for AssertUnwindSafe<T>`](https://doc.rust-lang.org/stable/std/panic/struct.AssertUnwindSafe.html#impl-From%3CT%3E-for-AssertUnwindSafe%3CT%3E) |
| 91 | +- [`From<T> for LazyCell<T, F>`](https://doc.rust-lang.org/stable/std/cell/struct.LazyCell.html#impl-From%3CT%3E-for-LazyCell%3CT,+F%3E) |
| 92 | +- [`From<T> for LazyLock<T, F>`](https://doc.rust-lang.org/stable/std/sync/struct.LazyLock.html#impl-From%3CT%3E-for-LazyLock%3CT,+F%3E) |
| 93 | +- [`core::range::RangeToInclusive`](https://doc.rust-lang.org/stable/core/range/struct.RangeToInclusive.html) |
| 94 | +- [`core::range::RangeToInclusiveIter`](https://doc.rust-lang.org/stable/core/range/struct.RangeToInclusiveIter.html) |
| 95 | +- [`core::range::RangeFrom`](https://doc.rust-lang.org/stable/core/ops/struct.RangeFrom.html) |
| 96 | +- [`core::range::RangeFromIter`](https://doc.rust-lang.org/stable/core/ops/struct.RangeFromIter.html) |
| 97 | +- [`core::range::Range`](https://doc.rust-lang.org/stable/std/range/struct.Range.html) |
| 98 | +- [`core::range::RangeIter`](https://doc.rust-lang.org/stable/std/range/struct.RangeIter.html) |
| 99 | + |
| 100 | +### Two Cargo advisories |
| 101 | + |
| 102 | +Rust 1.96 contains fixes for two vulnerabilities for users of third-party registries. |
| 103 | + |
| 104 | +- [CVE-2026-5223](https://blog.rust-lang.org/2026/05/25/cve-2026-5223/) is a **medium** severity vulnerability regarding extraction of crate tarballs with symlinks. |
| 105 | + |
| 106 | +- [CVE-2026-5222](https://blog.rust-lang.org/2026/05/25/cve-2026-5222/) is a **low** severity vulnerability regarding authentication with normalized URLs. |
| 107 | + |
| 108 | +Users of crates.io are **not affected** by either vulnerability. |
| 109 | + |
| 110 | +### Other changes |
| 111 | + |
| 112 | +Check out everything that changed in [Rust](https://github.com/rust-lang/rust/releases/tag/1.96.0), [Cargo](https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-196-2026-05-28), and [Clippy](https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#rust-196). |
| 113 | + |
| 114 | +## Contributors to 1.96.0 |
| 115 | + |
| 116 | +Many people came together to create Rust 1.96.0. We couldn't have done it without all of you. [Thanks!](https://thanks.rust-lang.org/rust/1.96.0/) |
0 commit comments