
The cfg-if crate has 67 million downloads per month. It lives inside the Linux kernel Rust bindings, inside Bevy, inside tokio, inside virtually every cross-platform Rust project worth mentioning. It is one of those dependencies that travels quietly in the background of your Cargo.lock without anyone thinking much about it. Rust 1.95, released April 16, 2026, just made it optional.
The standard library now ships cfg_select! — a native macro that does what cfg-if does, without the external dependency. That’s the headline. But 1.95 also brought if-let guards in match expressions and a meaningful wave of const API stabilizations. And the latest project goals update from the Rust team (published May 2026) shows the next-generation trait solver is making its final approach. Here’s what you need to know.
cfg_select!: Drop the Dependency, Keep the Behavior
cfg_select! evaluates a series of cfg predicates at compile time, top to bottom, and emits only the first matching branch. Think of it as a compile-time match on your build configuration. Add a _ wildcard as a fallback; leave it out and the compiler errors if nothing matches — exactly the kind of strictness you want in platform-specific code.
The migration from cfg-if is mostly a syntax swap. The semantics are identical; the difference is aesthetic and architectural. Here’s a direct comparison:
// Before: requires cfg-if in Cargo.toml
cfg_if::cfg_if! {
if #[cfg(unix)] {
fn get_path_sep() -> char { '/' }
} else if #[cfg(windows)] {
fn get_path_sep() -> char { '\\' }
} else {
fn get_path_sep() -> char { '/' }
}
}
// After: Rust 1.95+, zero deps needed
cfg_select! {
unix => { fn get_path_sep() -> char { '/' } }
windows => { fn get_path_sep() -> char { '\\' } }
_ => { fn get_path_sep() -> char { '/' } }
}
cfg_select! also works in expression position, which cfg-if handles awkwardly at best:
const PLATFORM: &str = cfg_select! {
unix => "unix",
windows => "windows",
wasm32 => "wasm",
_ => "other",
};
The Bevy game engine filed an issue immediately after 1.95 landed to migrate all their cfg_if blocks to the new macro. If one of the largest Rust game engines is treating this as immediate housekeeping, you probably should too. It is a mechanical change that removes a dependency and aligns you with stdlib conventions.
if-let Guards: Flatter Match Arms
Rust 1.88 stabilized let chains — the ability to write if let A = x && let B = y. Rust 1.95 brings that capability into match guard position. You can now write if let directly in the guard of a match arm, making both the matched variable and the result of a fallible operation available in the arm body without nesting.
// Before 1.95: nested if inside the arm body
match incoming {
Event::Message(raw) => {
if let Ok(cmd) = parse(&raw) {
dispatch(cmd);
}
}
_ => {}
}
// After 1.95: if-let in the guard itself
match incoming {
Event::Message(raw) if let Ok(cmd) = parse(&raw) => {
dispatch(cmd); // both raw and cmd in scope
}
Event::Message(_) => { /* message arrived but parse failed */ }
_ => {}
}
For anyone writing state machines, protocol parsers, or event handlers in Rust — which is a meaningful chunk of the ecosystem — this eliminates a class of awkward nesting. The compiler does not yet incorporate the if-let pattern into exhaustiveness analysis, consistent with how regular guards behave, but the ergonomic win is real regardless.
Const APIs: More Work at Compile Time
Rust 1.95 also expanded what you can do inside const fn without touching nightly. fmt::from_fn, ControlFlow::is_break(), and ControlFlow::is_continue() are now stable in const contexts. So are MaybeUninit array conversions, several Layout utilities, and atomic update methods. Each one removes a reason to reach for the nightly toolchain in low-level library code.
The Next-Gen Trait Solver: Final Approach
The Rust team’s next-generation trait solver — in development since late 2022 — is making measurable progress toward full stabilization. It has already replaced the old solver for coherence checking (since Rust 1.84) and is now active in lints and rustdoc. The remaining blocker is performance: canonicalization work is ongoing, and a crater run is underway to catch regressions.
Stabilizing -Znext-solver=globally is what finally unlocks a list of features blocked for years: Type Alias Impl Trait at full power, Return Type Notation for async traits, negative impls, and stronger const generics support. It also fixes soundness holes in the current solver — edge cases where the type system accepts code it should not. The new solver is not about runtime speed. It is about making the type system behave consistently with its own rules. That matters more than it sounds, and it’s closer than it’s ever been.
One Note for Embedded Developers
Custom JSON target specifications are now destabilized on stable Rust — they require -Z unstable-options and therefore the nightly toolchain. This affects a narrow audience (exotic embedded targets and OS development), since building core for a custom target already required nightly in practice. Cargo now has a -Z json-target-spec flag that passes the unstable option automatically. Everyone else: this does not touch you.
What to Do Today
Run rustup update stable and bump your MSRV to 1.95 when your release policy allows it. Then search your codebase for cfg_if::cfg_if! calls and start converting them — the change is mechanical, the diff is clean, and you eliminate an external dependency in the process. Track the official Rust release blog for the next-gen trait solver landing; when it stabilizes globally, the type system improvements arrive without any action on your part.
Rust’s ergonomics story has improved every release for years. cfg_select! is not a landmark feature, but it is the kind of quality-of-life improvement that, accumulated over time, is why developers keep choosing Rust for new projects rather than reverting to C or Go for the parts that used to be painful.













