Rust 1.98 landed on August 20 with a headline feature the previews missed: algebraic floating-point methods that directly address the long-standing gap where Rust dot products ran 8x slower than C++ on x86_64 CPUs. If you write numerical, audio, graphics, or ML inference code in Rust, this is the release you have been waiting for. Run rustup update stable
and read on.
The Float Problem: Why Rust Was Losing to C++ #
In 2025, a GitHub issue demonstrated that a simple Rust dot-product loop ran 8x slower than the equivalent C++ on modern x86_64 hardware. The culprit was not Rust’s optimizer — it was correctness. Rust strictly follows IEEE 754, which mandates that floating-point additions execute left to right. That means a + b + c + d
must be evaluated as ((a + b) + c) + d
, one operation at a time.
C++ with -O3
relaxes that constraint and lets the compiler reorder operations into (a + b) + (c + d)
, which maps directly to SIMD parallel execution. That is where the 8x gap came from. The previous Rust workaround — fadd_fast
intrinsics — assumed all inputs were finite, causing undefined behavior if a NaN or infinity appeared. Not exactly production-safe.
Algebraic Methods: Opt-In Performance, No Undefined Behavior #
Rust 1.98 adds five new methods to both f32
and f64
: algebraic_add
, algebraic_sub
, algebraic_mul
, algebraic_div
, and algebraic_rem
. They signal to the compiler that it may reorder these specific operations for better vectorization. Results may differ slightly from strict IEEE sequential execution — but they are never undefined behavior. The compiler picks valid floating-point values, not garbage.
Here is what a vectorization-friendly dot product looks like in Rust 1.98:
fn dot(a: &[f32], b: &[f32]) -> f32 {
a.iter().zip(b).fold(0.0_f32, |acc, (x, y)| {
acc.algebraic_add(x.algebraic_mul(*y))
})
}
That is the entire change. The compiler now has permission to generate vectorized code for this loop. Performance gains depend on your workload — simple accumulations can approach the C++ baseline, and numerical kernels previously bottlenecked on sequential float ops should see meaningful improvements.
This is a more surgical design than C++’s -ffast-math
, which applies to every float operation in the compilation unit — you cannot opt specific hot loops in while leaving the rest IEEE-compliant. Rust’s algebraic methods are per-operation, giving you precise control. Python Speed’s breakdown walks through the performance implications in detail.
format_into: Remove itoa from Your Cargo.toml #
The second headline addition is format_into
on all primitive integer types, paired with the new NumBuffer
type. It is a stack-allocated, zero-heap, zero-dynamic-dispatch way to turn integers into string slices:
let mut buf = NumBuffer::<u64>::INIT;
let s: &str = 1234567u64.format_into(&mut buf);
The itoa crate has served this purpose for years and logs around 200 million downloads per month on crates.io.
format_into
benchmarks on par with it. For new code using primitive integers in performance-critical paths, the stdlib now covers this use case without an external dependency. The itoa
crate still makes sense for its byte-slice API or broader type coverage, but most users can reach for stdlib instead.## Other Additions Worth Noting
Rust 1.98 also stabilizes String::from_utf16le
and String::from_utf16be
for explicit-endian UTF-16 conversion — a common pain point when processing Windows-native data formats. The strip_circumfix
method removes a matching prefix and suffix in a single call. And str::substr_range
/ [T]::subslice_range
return the index range of a sub-slice within its parent, simplifying several common string manipulation patterns.
The type system also gains the ability to shorten &mut
lifetimes when unsize-coercing in invariant positions — a narrow but previously frustrating limitation that forced some generic code into unsafe workarounds.
When Not to Use Algebraic Methods #
Algebraic methods are not appropriate for financial calculations, cryptographic code, or anywhere exact IEEE 754 reproducibility matters. If your test suite verifies exact floating-point output or you are implementing a spec that mandates sequential evaluation, leave algebraic_*
alone. For simulation, inference, audio processing, and rendering — anywhere approximate-but-faster is the right tradeoff — this is a clear upgrade.
Upgrading #
Rust 1.98 is a standard stable release with full backward compatibility. Existing code compiles without changes. The algebraic methods and format_into
are available immediately with no feature flags required:
rustup update stable
The full release announcement is on the official Rust blog. The complete API changelog lives at releases.rs. Coverage on Phoronix digs into the vectorization angle further.