Rust 1.95 makes conditional selection more explicit
cfg_select! selects one implementation at compile time, while if-let guards refine runtime match arms. Their similar appearance hides different rules for emitted code, fallback, and exhaustiveness.
Platform-specific code often needs an ordered choice between implementations. Independent configuration attributes can express that choice, but later branches need to exclude the conditions already handled earlier. Rust 1.95 introduces cfg_select! to describe the order directly. The April 16 announcement presents it alongside if-let guards in match expressions, another improvement in how code expresses conditional selection.
The two additions operate at different times. cfg_select! decides which source tokens to emit during compilation. A match guard decides whether an already matched runtime value satisfies another condition. Understanding that difference avoids treating both features as interchangeable spelling for an ordinary conditional.
I like Rust's ability to make constraints visible in the expression that needs them. These additions fit that preference when they replace repeated exclusions or nested matching with one readable decision. They are useful because their boundaries are specific, including what happens when no condition succeeds.
Configuration selection emits the first matching arm
The Rust 1.95 macro documentation defines an ordered sequence of configuration predicates. The macro emits the code from the first true predicate. Other arms do not emit code. A trailing underscore provides an optional fallback, and no matching predicate without a fallback produces a compile error.
This is more than grouping several independent cfg attributes. Suppose a Unix target also has 32-bit pointers. If the Unix predicate appears first, that implementation wins. The pointer-width arm is reached only when the earlier predicate fails. The ordering is part of the configuration policy and does not require the author to restate its exclusions in every later predicate.
The macro also works in expression position. A small example can make a build-time label follow the same ordered selection.
let platform = cfg_select! {
unix => "unix",
windows => "windows",
_ => "other",
};
The fallback is appropriate here because the example has a meaningful label for other targets. For a platform operation that has no correct fallback, omitting it can make an unsupported build fail explicitly. Supplying a generic-looking implementation that does not meet the operation's requirements would defeat that useful failure mode.
cfg! evaluates a boolean without removing its branches
The distinction from cfg! matters for platform code. The versioned cfg! documentation says it evaluates a configuration expression to a boolean but removes no code. Both branches of an if using that boolean must still be valid. A false branch cannot freely name an API that does not exist on the current target.
cfg_select! instead selects the tokens to emit. That allows an implementation branch to contain code specific to its configuration without requiring every other branch to compile for the same target. A build-time boolean and build-time source selection therefore solve different problems even when their predicates look identical.
This matters to a library that supports several operating systems. The common interface can remain near its implementations, with the selection order visible in one place. The macro does not establish that those implementations are semantically equivalent. It makes the source-selection rule explicit so that each implementation can be assessed against the same contract.
The release announcement identifies cfg-if as an existing crate serving the same purpose with different syntax. The new standard macro removes the need for a separate dependency for this particular operation. It does not imply that existing users must rewrite their configuration branches immediately or that every configuration helper becomes redundant.
A let guard adds bindings to a successful match arm
The Rust 1.95 match reference describes guard evaluation after an arm's initial pattern succeeds. A let pattern can match another expression and introduce bindings used in later guard conditions or the arm body. If the guard fails, matching continues to the next applicable pattern.
The release announcement illustrates an optional value whose successful branch also requires another computation to return Ok. Both the original value and the computed result become available in the arm. The condition expresses the actual requirement for entering that branch without an additional nested match inside its body.
This can make the remaining cases clearer too. Failure of the added condition does not force the code to handle everything inside the first arm. A later arm can express the fallback for the same outer variant. The order of arms still determines which applicable behavior runs, so moving a guarded arm below an unguarded arm can change the result. Keeping the guarded case adjacent to its fallback lets a reader see both outcomes of the additional computation without searching through a nested block for the alternative behavior.
Guards refine execution without proving exhaustive coverage
The release notes explicitly state that patterns inside if-let guards do not contribute to the compiler's exhaustiveness analysis. They behave like ordinary guards for that purpose. A programmer cannot use a collection of guarded arms as a substitute for the coverage the compiler requires from the match patterns themselves.
That boundary is sensible for reading the source. The initial pattern describes which value shapes an arm can consider. The guard can perform a further computation whose success depends on runtime behavior. Combining those facts into one expression improves locality without turning the computation into a complete structural proof of coverage.
Guard chains also have defined runtime execution order. The reference specifies left-to-right evaluation with short circuiting after a false condition or failed let pattern. It warns that alternatives joined by the pattern | operator can evaluate a guard more than once. A guard with side effects therefore needs the same attention to execution count as other repeated control flow.
For me, that is the useful balance in this release. Rust adds concise ways to state selection while preserving explicit rules for compilation and runtime execution. Configuration fallback can fail the build when no implementation exists. Match fallback remains part of the runtime value handling, and the compiler continues to demand exhaustive structural coverage independently of the guard's computation.