What Is New in Rust 1.98
| Category | Highlights |
|---|---|
| New Features |
|
| Improvements |
|
| Bug Fixes |
|
| Breaking Changes |
|
What language changes are new in Rust 1.98?
The headline language change in Rust 1.98 lets the compiler shorten the lifetime of a &mut reference during unsize-coercion, even when that reference sits in an invariant position. In practice, this closes a gap that previously only applied to &mut -> & and & -> & coercions.
Concretely, you can now write code that coerces Cell<&'long mut i32> into Cell<&'short mut dyn Send> without extra lifetime gymnastics. This matters if you build generic wrapper types around mutable references and have hit borrow-checker friction when trying to erase a concrete type into a trait object while shortening its lifetime.
fn shorten<'long: 'short, 'short>(cell: &Cell<&'long mut i32>) -> &Cell<&'short mut dyn Send> {
cell
}
Watch out for the accompanying compatibility note: fully elided lifetime bounds on trait object types may now resolve differently, or even get rejected, in very specific niche scenarios. Most codebases will not notice this, but it is worth a quick look if you rely heavily on elided lifetimes with trait objects.
Which new lints should Rust 1.98 users watch for?
Rust 1.98 introduces two lints aimed at catching mismatched or unsafe redefinitions of core runtime symbols. The deny-by-default invalid_runtime_symbol_definitions lint and the warn-by-default suspicious_runtime_symbol_definitions lint currently target symbols such as memcmp, memset, and strlen, with more symbols planned for future releases.
This matters if you write no_std or embedded code that provides custom implementations of these C runtime functions. Most teams working in application-level Rust will never trigger these lints, but low-level crates that shadow libc symbols should compile with the new toolchain early to catch any deny-level failures before they hit CI.
A third lint, c_void_returns, is now warn-by-default and flags functions that return core::ffi::c_void directly. This usually signals an FFI binding mistake, since c_void is meant to be used behind a pointer, not as a return type on its own.
What platform support changes come with Rust 1.98?
Rust 1.98 expands both the Tier 3 and Tier 2 target lists, with the biggest practical win being for embedded ARM development. thumbv7a-none-eabi, thumbv7a-none-eabihf, thumbv7r-none-eabi, thumbv7r-none-eabihf, and thumbv8r-none-eabihf are all promoted to Tier 2, which means official builds and more reliable CI coverage from the Rust project.
powerpc64-unknown-linux-gnuelfv2added as Tier 3aarch64-unknown-linux-pauthtestadded as Tier 3, useful for testing pointer authentication on AArch64- The five thumbv7/thumbv8 embedded targets promoted from Tier 3 to Tier 2
In practice, if you ship firmware or RTOS-based projects on Cortex-A or Cortex-R cores, this promotion means you can rely on prebuilt targets from rustup instead of building your own toolchain from source.
What breaking changes affect upgrading to Rust 1.98?
Most Rust 1.98 breaking changes are narrow, but a few are worth testing against before you roll the update out fleet-wide. The most likely one to bite real projects is the fast-path implementation of derived Ord that now builds on derive(PartialOrd).
- If a type's manual or derived
PartialOrdandOrdimplementations were already inconsistent, this can surface as different sort or comparison behavior after upgrading repr(transparent)layout checks got stricter:repr(C)types, types with private fields, and#[non_exhaustive]types are no longer treated as having a trivial layout- Where-bounds written with
=or==between types are now a hard parse error rather than accepted syntax ambiguous_glob_importsnow errors in more situations, so wildcard-import-heavy modules are worth a quick recompile check
This matters if you maintain a large crate with legacy glob imports or older derive-based ordering logic. Most teams should run a full test suite against 1.98 in CI before merging the toolchain bump, rather than assuming a patch-level upgrade is safe.
What new stable APIs are available in Rust 1.98?
Rust 1.98 stabilizes a useful batch of string, slice, and numeric APIs that reduce the need for manual index math or third-party crates. str::substr_range and [T]::subslice_range let you recover the byte range of a substring or subslice relative to its parent, which is handy when building diagnostics or parsers.
String::from_utf16le,from_utf16le_lossy,from_utf16be, andfrom_utf16be_lossyfor endian-aware UTF-16 decoding without pulling in an external crate{fN}::algebraic_add,algebraic_sub,algebraic_mul,algebraic_div, andalgebraic_remfor floating point operations that permit reassociation-friendly optimizationsAtomic,::from_mut from_mut_slice, andget_mut_slicefor converting between atomic and non-atomic referencesNonZero<{integer}>::from_str_radixand{integer}::format_intofor parsing and formatting without extra allocations
In practice, the UTF-16 constructors and the substring range helpers are the two most likely to replace hand-rolled utility code in existing codebases.
FAQ: Common Questions about Rust 1.98
Does upgrading to Rust 1.98 require changes to existing code?
Most codebases compile unchanged, but projects relying on inconsistent PartialOrd and Ord implementations, older where-bound syntax, or loose repr(transparent) layouts should run their test suite before rolling the upgrade out broadly.
What is the new mut reference coercion rule in Rust 1.98?
Rust 1.98 allows the lifetime of a mut reference to shorten during unsize coercion even in invariant position, for example coercing Cell of a long lived mut i32 reference into Cell of a shorter lived mut dyn Send reference.
Will the new runtime symbol lints break my build?
Only if your crate redefines core runtime symbols like memcmp, memset, or strlen incorrectly, since invalid_runtime_symbol_definitions is deny by default and will fail compilation in that case.
Are any Rust targets promoted in 1.98?
Yes.
Why did my derived Ord behavior change after upgrading?
Rust 1.98 implements a fast path for derive Ord that is built directly from derive PartialOrd, so any type whose manual or derived implementations of the two traits were already inconsistent may now sort or compare differently.
What happened to the emscripten-wasm-eh flag?
It was removed because Rust 1.98 unconditionally uses the WASM exception handling ABI on Emscripten targets instead of JS exceptions.