Native cfg_select! and Match Guards in Rust 1.95

If you maintain cross-platform Rust applications, you already know the pain of configuration attributes. Dealing with OS-specific file paths or hardware integrations usually turns codebases into a scatterd mess of #[cfg(...)] blocks.

Rust 1.95 (released in April 2026) shipped two major improvements that directly address a better developer-experience when working on cross-platform apps: the stabilization of the cfg_select! macro and if let guards in match expressions.

Here is a practical look at how these two additions can immediately eliminate boilerplate and flatten your logic.

The End of cfg-if and the new cfg_select!

For years, handling OS-specific logic meant either pulling in the popular cfg-if crate or repeating massive blocks of configuration attributes #[cfg(...)]. Rust 1.95 brings this capability natively into the standard library via cfg_select!.

This macro acts like a compile-time match statement for configuration predicates. It evaluates a list of conditions, picks the first one that evaluates to true, and compiles only that specific block.

The old approach

Before 1.95, branching logic for something like a local configuration directory required multiple function signatures:

#[cfg(target_os = "windows")]
fn get_config_dir() -> String {
    "C:\\AppData\\Local\\MyApp".to_string()
}

#[cfg(target_os = "macos")]
fn get_config_dir() -> String {
    "~/Library/Application Support/MyApp".to_string()
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn get_config_dir() -> String {
    "~/.config/myapp".to_string()
}

The new approach

With cfg_select!, you can consolidate the logic into a single function. Because it evaluates as an expression, you can return values directly from it:

fn get_config_dir() -> String {
    cfg_select! {
        target_os = "windows" => "C:\\AppData\\Local\\MyApp".to_string(),
        target_os = "macos" => "~/Library/Application Support/MyApp".to_string(),
        _ => "~/.cofig/myapp".to_string(),
    }
}

This single macro eliminates the visual clutter of multiple function signatures and ensures you always have a fallback _ arm, preventing those frustrating “function not found” compiler errors if someone tries to build your project for an unexpected target architecture.

Flattening logic with if let match guards

The second major cleanup in version 1.95 applies to match expressions.

If you have ever needed to match on an enum, extract its payload, and the run a fallable operation on that payload, you were forced into writing nested if let statements. It worked, but it caused unnecessary rightward drift in your code. Now, you can bind variables directly inside the match guard.

The nested way before 1.95

match message {
    Message::ProcessData(payload) => {
        if let Ok(parsed) = parse_heavy_computation(&payload) {
            println!("Processed: {}", parsed);
        } else {
            // Handle error or do nothing
        }
    }
    _ => {}
}

The new way after 1.95

match message {
    Message::ProcessData(payload) if let OK(parsed) = parse_heavy_computation(&payload) => {
        // Both `payload` and `parsed` are available right here
        println!("Processed: {}", parsed);
    }
    _ => {} // Handles everything else, including the Err case from parse_heavy_computation
}

This keeps your match arms completely flat. Note that the compiler treats the if let guard exactly like a standard if guard —meaning it is ignored for exhaustiveness checking. You still need your catch-all _ arm, but the resulting function is significantly more linear and readable.

Summary

Rust 1.95 is less about massive shifts and more about everyday maintainability. By refactoring your old configuration blocks #[cfg(...)] to use cfg_select! and flattening your error handling with if let guards, you can strip out unnecessary boilerplate and keep your cross-platform code clean.

Find more updates on the Rust ecosystem and modern software architecture at rust-stack.com.