DEV Community

Matheus
Matheus

Posted on • Originally published at releaserun.hashnode.dev

Rust 1.94.0: array_windows, Cargo Config Includes, and 10 Breaking Changes You Should Know About

Rust 1.94.0 landed on March 5, 2026. Three headline features and a surprisingly long compatibility notes section.

Here's what actually matters if you're shipping Rust in production.

The Headlines

array_windows Finally Stabilized

This one's been cooking since 2020. array_windows gives you sliding window iteration over slices, but with compile-time known sizes instead of runtime slices.

// Old way: runtime-sized windows, manual indexing
data.windows(4).any(|w| w[0] != w[1] && w[0] == w[3] && w[1] == w[2])

// New way: destructure directly, compiler knows the size
data.as_bytes()
    .array_windows()
    .any(|[a1, b1, b2, a2]| (a1 != b1) && (a1 == a2) && (b1 == b2))
Enter fullscreen mode Exit fullscreen mode

The real win here isn't just ergonomics. The compiler can eliminate bounds checks entirely because it knows the window size at compile time. If you're doing any kind of signal processing, pattern matching, or rolling calculations over slices, this is a free performance upgrade.

The window size is inferred from usage too. That destructuring pattern |[a1, b1, b2, a2]| tells the compiler you want windows of 4. No need to specify it explicitly.

Cargo Config Includes

You can now split your .cargo/config.toml across multiple files:

include = [
    { path = "ci.toml" },
    { path = "local-overrides.toml", optional = true },
]
Enter fullscreen mode Exit fullscreen mode

This is genuinely useful for teams. You can keep CI-specific settings, local developer overrides, and shared config separate without fighting merge conflicts in one massive config file. The optional = true flag means you can have developer-specific files that don't need to exist for everyone.

Monorepo teams will probably get the most out of this. Think shared build profiles, registry mirrors, or target-specific settings that only some developers need.

Also worth noting: Cargo now records a pubtime field in the registry index, tracking when each crate version was published. This lays groundwork for time-based dependency resolution in the future. crates.io is gradually backfilling existing packages.

TOML 1.1 in Cargo

Cargo now parses TOML v1.1, which means you can finally write multi-line inline tables:

# Before: everything crammed on one line
serde = { version = "1.0", features = ["derive", "rc", "alloc"] }

# After: readable and trailing commas allowed
serde = {
    version = "1.0",
    features = [
        "derive",
        "rc",
        "alloc",
    ],
}
Enter fullscreen mode Exit fullscreen mode

One catch: if you use TOML 1.1 syntax in your Cargo.toml, your development MSRV effectively becomes Rust 1.94. Cargo rewrites the manifest on publish to stay compatible with older parsers, so your users won't be affected. But anyone building your crate from source with an older toolchain will hit parse errors.

If you're maintaining a library with a strict MSRV policy, hold off on the new syntax for now.

Breaking Changes: The Real Release Notes

This is where 1.94 gets interesting. Ten compatibility notes, and some of them will bite you.

Closure Capturing Behavior Changed

The biggest one. How closures capture variables around pattern matching has been tightened up. Previously, a non-move closure might capture an entire variable by move in some pattern matching contexts. Now it captures only the parts it needs.

Sounds good in theory, but it can cause new borrow checker errors where code previously compiled fine. It can also change when Drop runs for partially captured values.

If you have closures near match or if let expressions that suddenly stop compiling after upgrading, this is likely why.

Standard Library Macros Import Change

Standard library macros (println!, vec!, matches!, etc.) are now imported via the prelude instead of #[macro_use]. This sounds like an internal change, but it has a visible effect: if you have a custom macro with the same name as a standard library macro and you glob-import it, you'll get an ambiguity error.

The most common case: if you defined your own matches! macro and glob-imported it. You'll need an explicit import to resolve which one you mean.

For #![no_std] code that glob-imports from std, you might see a new ambiguous_panic_imports warning because both core::panic! and std::panic! are now in scope.

dyn Trait Lifetime Casting Restricted

dyn trait objects can no longer freely cast between different lifetime bounds. If you were doing something like casting dyn Foo + 'a to dyn Foo + 'b, the compiler now correctly rejects it.

Shebang Lines in include!()

include!() in expression context no longer strips shebang lines (#!/...). If you were including files that start with a shebang, they'll now fail to compile. The fix is to remove the shebang from included files.

Other Compat Notes

  • Ambiguous glob reexports are now visible cross-crate (may introduce new ambiguity errors in downstream crates)
  • Where-clause normalization changed in well-formedness checks
  • Codegen attributes on body-free trait methods now produce a future compatibility warning (they had no effect anyway)
  • Windows SystemTime changes: checked_sub_duration returns None for times before the Windows epoch (Jan 1, 1601)
  • Lifetime identifiers are now NFC normalized (e.g. 'รก written with combining characters vs precomposed). Edge case, but if you're generating Rust code programmatically, double check.
  • Compiler filename handling overhauled for cross-compiler consistency. Paths in diagnostics for local crates in Cargo workspaces are now relative instead of absolute. This can break CI scripts that grep compiler output for absolute paths.

Stabilized APIs Worth Knowing

Beyond array_windows, a few other stabilizations stand out:

LazyCell::get and LazyLock::get: Check whether a lazy value has been initialized without forcing it. Useful for conditional logic around cached values.

Peekable::next_if_map: Conditionally advance a peekable iterator and transform the value in one step. Cleaner than peek + next + map separately.

element_offset: Get the index of an element in a slice from a reference to it. Handy when you have a reference into a slice and need to know where it is.

f32/f64::consts::EULER_GAMMA and GOLDEN_RATIO: Mathematical constants added to the standard library. Minor, but saves you from defining them yourself.

f32/f64::mul_add now const: Fused multiply-add in const contexts. Useful for compile-time math.

Platform and Compiler Notes

  • New tier 3 target: riscv64im-unknown-none-elf (RISC-V without atomics)
  • 29 additional RISC-V target features stabilized, covering large parts of RVA22U64 and RVA23U64 profiles
  • Unicode 17 support
  • BinaryHeap methods relaxed: some no longer require T: Ord (for methods that don't need ordering)
  • Error messages now use annotate-snippets internally, so diagnostic output may look slightly different

Upgrade Recommendation

Rust 1.94 is a solid release. array_windows alone makes it worth upgrading if you do any slice processing. The Cargo improvements are pure quality of life.

The main risk is the closure capturing change. If you have a large codebase, run cargo check before deploying and watch for new borrow checker errors around closures. The macro import change is lower risk but check if you have any custom macros that shadow stdlib names.

rustup update stable
cargo check  # Run this before committing to the upgrade
Enter fullscreen mode Exit fullscreen mode

For teams on MSRV policies: 1.94 is safe to adopt as your new MSRV if you want the Cargo improvements. If you're maintaining a library, consider waiting one release cycle (until 1.95) to let the closure capturing changes settle and for downstream users to upgrade.

ReleaseRun Health Grade: A (actively maintained, 6-week release cadence, no EOL concerns)


Track Rust and 300+ other technologies at releaserun.com. Get version health grades, EOL alerts, and upgrade recommendations for your entire stack.


Keep Reading

Top comments (0)