tech, developers, and the code underneath

issue 084· news·

Rust 1.91 and the slow work of shrinking unsafe

More const, more stable APIs, and an ecosystem that keeps finding ways to need less unsafe code.

Rust 1.91 shipped this week with the usual batch of API stabilizations and const improvements. Rather than enumerate them, it is worth looking at the trend they are part of, because it is the most underrated thing about Rust's development.

the pattern#

A large fraction of Rust's per-release API additions exist to let you delete an unsafe block.

Some recent examples across the last year of releases:

  • HashMap::get_disjoint_mut — previously required unsafe or a clumsy dance.
  • <[T]>::as_chunks — previously a transmute or a manual loop with unchecked indexing.
  • Vec::extract_if — previously either an allocation or unsafe in-place work.
  • std::io::pipe — previously platform-specific unsafe FFI.
  • Const-stabilized functions across the library — previously a lazy_static or a build script.

None of these are exciting individually. Cumulatively they are the mechanism by which the amount of unsafe code in the average Rust program keeps going down.

why this matters more than features#

The value proposition of Rust is memory safety without a garbage collector. Every unsafe block is a hole in that guarantee — a place where the compiler stops checking and a human is asserting correctness.

Studies of real-world Rust code consistently find that most unsafe usage falls into a small number of patterns, and that a majority of those patterns exist because the safe standard library did not offer the operation.

So the library team's strategy is: find the patterns, provide safe equivalents, watch the unsafe count fall. It works, and it is measurable.

The remaining unsafe concentrates in the places where it belongs — FFI, hardware interaction, and hand-optimized data structures — where it is reviewed by people who know what they are doing, in crates that are audited.

the const story specifically#

Const stabilization is the other steady drip. Every release moves more functions into const fn, meaning they can run at compile time.

The practical effect is that more computation moves from runtime to compile time, and more static data can be computed rather than hand-written. Lookup tables, parsed configuration, precomputed constants — all of it can now be expressed as code that runs during compilation.

rust
const TABLE: [u32; 256] = {
    let mut t = [0u32; 256];
    let mut i = 0;
    while i < 256 { t[i] = crc_entry(i as u8); i += 1; }
    t
};

That would have required a build script or a generated file a few years ago.

the honest limitations#

Const generics still cannot do arithmetic in type position without nightly. Async in traits works but has sharp edges around lifetimes and Send bounds. The GUI ecosystem remains unsettled after a decade of attempts.

None of those are getting fixed this release, or probably next release. Rust's development model trades speed for not making mistakes it cannot undo, and the consequence is that the hard problems stay open for a long time.

That is a defensible trade for a language with a stability guarantee, and it is genuinely frustrating if you are blocked on one of them.

the meta-observation#

If you want to evaluate a language's health, do not look at its feature announcements. Look at whether the amount of dangerous code required to do ordinary things is going up or down.

By that measure Rust is doing better than almost anything else, and it is doing it in increments so small that nobody writes headlines about them.

Dom, October 31, 2025

get README in your inbox

One dispatch, no noise. Tech and developer news, plus the occasional long piece on the craft.

subscribe →