TL;DR
- Rust 1.96.0 shipped 2026-05-28. The WebAssembly linker change is the one that breaks builds: it no longer passes
--allow-undefined, so an undefined symbol is a linker error instead of being converted to an import from theenvmodule. - If you build a wasm target and you have undefined symbols that used to become
envimports, your build fails on the bump. Two ways back:RUSTFLAGS=-Clink-arg=--allow-undefined, or annotate the symbol with#[link(wasm_import_module = "env")]. - Also in: stabilized
assert_matches!/debug_assert_matches!, copyable range types incore::range(the std half of RFC 3550), and fixes for two Cargo CVEs that only touch third-party-registry users.
The wasm change, and who it breaks
Before 1.96.0, rustc passed --allow-undefined to the wasm linker. An undefined symbol at link time didn't fail the build — it got quietly turned into a WebAssembly import from the env module, and the host was expected to supply it at instantiation. Convenient, and a good way to ship a binary with a typo'd symbol name that nobody notices until the module fails to instantiate in production.
1.96.0 drops the flag. An undefined symbol is now a hard linker error. The release notes frame it as catching bugs earlier — symbol-naming mistakes and accidental missing definitions surface at build time instead of at instantiation.
Reader-action test: do you build a wasm target with symbols you expect the host to provide? If yes, and you were leaning on the old auto-import behavior, the bump turns those into link errors. You'll see it in CI the moment someone updates the toolchain.
What to change
Two supported paths, per the release notes:
- Restore the old behavior wholesale:
RUSTFLAGS=-Clink-arg=--allow-undefined. This re-adds the flag and you're back to undefined-becomes-env-import. Blunt, fine as a stopgap while you sort out which symbols were intentional. - Be explicit per symbol: put
#[link(wasm_import_module = "env")]on theexternblock declaring the symbol. This says "yes, this one is a host import fromenv" out loud, so the linker stops treating it as a mistake. This is the version you want to keep — it documents intent instead of disabling the check globally.
If your wasm build has no host imports at all, you likely won't notice anything; this only bites builds that depended on the implicit conversion.
The smaller stable bits
assert_matches! and debug_assert_matches! are stable. They assert a value matches a pattern and, on failure, panic with the Debug value that didn't match — so you get what the value was, not just that the assert blew up. They are not in the prelude, on purpose: the names collide with popular third-party crates, so you import them from core/std yourself.
Copyable range types land in core::range — the standard-library portion of RFC 3550. The motivation: people expect Range to be Copy, but the legacy ranges implement Iterator directly, and Copy + Iterator on one type is a footgun. The new types implement IntoIterator instead, so they can be Copy. Note the catch: 0..1 syntax still produces the legacy types for now; the switch to core::range is deferred to a future edition. So this is opt-in today, not a silent behavior change to existing range code.
The Cargo CVEs (secondary)
1.96.0 also carries fixes for two Cargo vulnerabilities, both scoped to third-party-registry users — crates.io users are unaffected by either:
- CVE-2026-5223 (medium): a crafted tarball's symlink could escape the crate's cache directory. Covered in an earlier osbytes note.
- CVE-2026-5222 (low): authentication against normalized URLs.
If you build crates from anything other than crates.io, the bump is also a patch. If you're crates.io-only, these are informational — the wasm change is your real reason to read the changelog before bumping.
What to do on bump
- Building wasm? Grep your tree for
externblocks that expect host-provided symbols. Decide per symbol: keep it (#[link(wasm_import_module = "env")]) or it was a mistake (fix the definition). Reach forRUSTFLAGS=-Clink-arg=--allow-undefinedonly as a temporary unblock. - Pin the CI toolchain bump in a branch first so the wasm link errors land in review, not on someone's
mainpush. - Third-party-registry users get the two Cargo fixes for free; crates.io users lose nothing.
Sources
- Announcing Rust 1.96.0 (Rust Blog, 2026-05-28 — version, release date, wasm
--allow-undefinedchange and both restore mechanisms,assert_matches!/debug_assert_matches!,core::range/ RFC 3550, the two Cargo CVE references) - CVE-2026-5223: Cargo does not check symlinks when packaging (Rust Security Response WG advisory — third-party-registry scope, crates.io exemption)