
Rust 1.93.0 release notes: SIMD, varargs, and the stuff that breaks builds
I’ve watched “minor” Rust upgrades stall a release train for one dumb reason. Emscripten flags.
Rust 1.93.0 lands with real wins for low-level work (SIMD on s390x, C-style variadic functions), plus a few changes that can trip CI in under 60 seconds if you ship WebAssembly or rely on sloppy tests.
The 30-second upgrade call
Upgrade if you hit FFI edges, ship on IBM Z, or you want stricter diagnostics before prod. Wait a week if your WebAssembly pipeline depends on Emscripten and you cannot spare an afternoon to chase linker flags.
- High risk: Emscripten unwinding ABI change for panic=unwind. Your build can fail at link time.
- Medium risk: Stricter #[test] validation. Rust stops ignoring invalid placements and starts erroring.
- Low risk: New lints and Cargo quality-of-life changes. You will mostly see warnings.
What actually changed (the parts you will notice)
This bit me when a “harmless” std behavior change hid in a patch note. BTreeMap::append now stops overwriting existing keys when the incoming map contains a key you already have.
- New lints: Rust now warns by default on const_item_interior_mutations and function_casts_as_integer. Expect fresh warnings in older codebases with clever const tricks or pointer-ish casts.
- Cargo clean: cargo clean –workspace now cleans every package in a workspace, not just the current one.
- Built-in attributes: Rust adds pin_v2 to the built-in attribute namespace.
- Future incompat warnings: Rust now warns about … parameters without a pattern (outside extern blocks), repr(C) enums with discriminants outside c_int/c_uint, and repr(transparent) that “forgets” an inner repr(C) type.
- musl bump: The bundled musl version moves to 1.2.5. This usually feels boring until your static builds stop matching yesterday’s container image.
SIMD on s390x: useful, but only if you live there
🔔 Never Miss a Breaking Change
Get weekly release intelligence — breaking changes, security patches, and upgrade guides before they break your build.
✅ You're in! Check your inbox for confirmation.
Most teams will not rewrite hot loops this quarter. Good.
If you run on IBM Z, stabilized s390x vector target features matter because they let you ship one binary that checks CPU features at runtime, then takes the fast path. The macro to look for is is_s390x_feature_detected!. The thing nobody mentions is the boring part: you still need a scalar fallback unless you control every machine you deploy to.
- Where it pays off: tight numeric code, compression, crypto-ish primitives, and batch processing where you touch big buffers.
- Where it wastes time: request routing, JSON glue, anything dominated by syscalls or allocations.
If you cannot test your CNI in staging, you should not be running Kubernetes. Same energy here. If you cannot test on the actual CPU, do not pretend your SIMD change helped.
C-style variadic functions: great for FFI, still a foot-gun
Variadics make FFI wrappers less awkward. They do not make them safe.
Rust 1.93.0 stabilizes declaring C-style variadic functions for the system ABI, which helps when you need to bind to APIs like printf-style functions. In most cases, you should wrap the variadic call in a tiny unsafe boundary and expose a non-variadic Rust API to the rest of your crate. Some folks skip that and export varargs directly. I do not.
- Good pattern: keep the extern varargs signature private, then build typed wrappers around the handful of formats you actually need.
- Bad pattern: re-export varargs in your public Rust API and hope callers pass the right types on every platform.
Breaking changes that deserve a real test run
Here’s the one that will ruin your afternoon. Emscripten.
Rust changed the Emscripten unwinding ABI from JS exception handling to wasm exception handling when you compile with panic=unwind. If you link C or C++ objects, you now need to pass -fwasm-exceptions to the linker. If you build wasm once a month, you will forget this and rediscover it the hard way.
- #[test] validation: Rust now errors when you slap #[test] on structs, trait methods, or other invalid spots. Older code that “worked” only worked because Rust ignored it.
- deref_nullptr lint: Rust upgrades deref_nullptr to deny-by-default. Builds can fail where they used to warn.
- offset_of! macro: offset_of! now validates user-written types for well-formedness. Code that relied on sketchy layouts might stop compiling.
- cargo publish output: cargo publish no longer leaves .crate files as a final artifact when build.build-dir is unset.
Known issues and the “annoying but real” corner cases
I’ve seen this show up as a clean compile on one machine and a dead build in CI. Environment variables.
A Cargo environment variable change around CARGO_CFG_DEBUG_ASSERTIONS can break projects that depend on static-init versions 1.0.1 to 1.0.3, typically with an unresolved module-style error. If your dependency tree includes static-init, test this upgrade before you merge a toolchain bump across all repos.
Other stuff in this release: dependency bumps, some image updates, the usual.
How I’d roll this out (without drama)
Pin the toolchain first. Seriously.
Update locally, then run the exact commands your CI runs, not the “happy path” build you do on your laptop. For prod systems, test this twice. For a dev sandbox, sure, yolo it on Friday.
- Update: rustup update stable
- Confirm: rustc –version and check for 1.93.0
- Build: cargo build –release
- Test: cargo test and watch for new #[test] errors
- Scan warnings: pay attention to const_item_interior_mutations and function_casts_as_integer
- CI: align every runner image and container to the same toolchain
Official notes
Read the upstream release notes on GitHub if you ship to weird targets or you maintain a library. That page holds the exact wording and linked PRs.
Anyway.
Keep Reading
Frequently Asked Questions
- What are the biggest changes in Rust 1.93.0? Three headline features: (1) SIMD intrinsics for s390x architecture (niche but important for IBM mainframe teams), (2) C-style variadic functions using extern “C” fn with … syntax for better FFI interop, and (3) several lint changes that may cause existing code to fail compilation. The lint changes are what most teams will actually notice during upgrades.
- Will Rust 1.93.0 break my existing code? Possibly. The function_casts_as_integer lint was elevated from warning to error, meaning code that casts function pointers to integers will now fail to compile. Check for patterns like fn_ptr as usize in your codebase. Additionally, some previously allowed implicit conversions in match arms are now flagged. Run cargo build with the new version in CI before committing to the upgrade.
- How do I safely upgrade Rust in CI? Pin your Rust version in rust-toolchain.toml (e.g., channel = “1.93.0”). Before upgrading: (1) Run cargo clippy with the new version to catch new warnings, (2) Run your full test suite, (3) Check for deprecation warnings that became errors. If anything breaks, you can temporarily allow specific lints with #[allow(lint_name)] while you fix the underlying code. Never upgrade Rust and merge in the same PR.
- What are C-style variadic functions in Rust 1.93.0 used for? They let you write Rust functions that accept a variable number of arguments, matching C’s printf(const char*, …) pattern. This is primarily useful for FFI (Foreign Function Interface) — if you’re writing a Rust library that needs to expose a C-compatible API, you can now define variadic functions directly instead of using workarounds. For pure Rust code, macros remain the idiomatic way to handle variable arguments.