Fbhchile

2026-05-20 14:49:43

How to Upgrade to Rust 1.95.0 and Leverage Its New Features

Step-by-step guide to upgrade to Rust 1.95.0 and use cfg_select! macro and if-let guards in match expressions, plus overview of stabilized APIs.

Introduction

Rust 1.95.0 is here, bringing new tools to make your code more expressive and efficient. This guide walks you through upgrading your Rust installation and putting the headline features—cfg_select! and if-let guards in match—to work in your projects. Whether you're maintaining a cross-platform library or writing complex pattern-matching logic, these additions will streamline your workflow. By the end of this tutorial, you'll be ready to use the latest stabilized APIs and write cleaner, more conditional code.

How to Upgrade to Rust 1.95.0 and Leverage Its New Features
Source: blog.rust-lang.org

What You Need

  • An existing Rust installation (any recent version) managed via rustup
  • A terminal or command prompt
  • Basic familiarity with Rust syntax, especially cfg attributes and pattern matching
  • A text editor or IDE of your choice

Step 1: Update to Rust 1.95.0

Open your terminal and run:

rustup update stable

This command will fetch the latest stable release (1.95.0) if you're on an older version. Verify the update with:

rustc --version

You should see output like rustc 1.95.0 (YYYY-MM-DD). If you don't have Rust installed yet, visit the official Rust website to get rustup, then repeat the update step.

Step 2: Master the cfg_select! Macro

Rust 1.95.0 introduces cfg_select!, a compile-time macro that selects the first arm whose configuration predicate evaluates to true. It's similar to the popular cfg-if crate but with its own syntax.

2.1 Understand the Syntax

The macro expects a series of arms, each with a configuration predicate (like unix, windows, or target_pointer_width = "32") followed by => and an expression or block. The special predicate _ acts as a catch-all fallback. For example:

cfg_select! {
    unix => {
        fn foo() { /* unix specific */ }
    }
    target_pointer_width = "32" => {
        fn foo() { /* 32-bit, non-unix */ }
    }
    _ => {
        fn foo() { /* fallback */ }
    }
}

You can also use it to produce values:

let is_windows_str = cfg_select! {
    windows => "windows",
    _ => "not windows",
};

2.2 Replace Your cfg-if Usage

If you were using the cfg-if crate, you can now replace it with cfg_select! inline. This removes a dependency and keeps your code self-contained. Simply wrap your conditional compilation logic in the macro call.

Tip: cfg_select! is evaluated at compile time, so it has zero runtime cost.

Step 3: Use if-let Guards in match Expressions

Rust 1.95.0 extends the if-let guard capability (previously stabilized in let chains) into match arms. This lets you combine pattern matching with a follow-up if let condition inside a single arm.

3.1 Basic Example

Consider you want to match on an Option and then conditionally bind a variable only if a computation succeeds:

match value {
    Some(x) if let Ok(y) = compute(x) => {
        // Both `x` and `y` are available here
        println!("{}, {}", x, y);
    }
    _ => {}
}

Here, the arm matches only if value is Some(x) and compute(x) returns Ok(y). Without this guard, you'd need a nested match or if let block.

3.2 Important Note on Exhaustiveness

The compiler currently does not consider patterns inside if let guards when checking exhaustiveness of the overall match. This is the same behaviour as regular if guards. Plan your arms accordingly—add a wildcard _ arm to handle all remaining cases.

Step 4: Explore the Stabilized APIs

Rust 1.95.0 also stabilizes a host of new APIs. Here are some highlights you can start using immediately.

4.1 Conversions and Traits for MaybeUninit

Several conversions between MaybeUninit<[T; N]> and arrays of MaybeUninit are now stable, along with AsRef and AsMut implementations. This makes it easier to work with uninitialized memory in arrays.

4.2 AtomicPtr::update and Friends

Atomic operations get update and try_update methods for AtomicPtr, AtomicBool, and all atomic integer types. These methods fetch the current value, apply a closure, and store the new value atomically.

4.3 bool: TryFrom<{integer}>

Convert any integer to a bool using TryFrom. Returns Ok(false) for 0, Ok(true) for 1, and an error for other values.

4.4 New Modules and Functions

  • mod core::range – exposes RangeInclusive and its iterator.
  • core::hint::cold_path – a hint to the optimizer that a code path is cold.
  • Unchecked dereference helpers<*const T>::as_ref_unchecked and similar for raw pointers.
  • Mutation methods for collectionsVec::push_mut, VecDeque::push_front_mut, LinkedList::push_front_mut, etc. These allow in-place mutation of elements without copying.

For the complete list, check the official release notes.

Tips and Best Practices

  • Test your code after upgrading – Run cargo test to ensure nothing broke due to the version bump.
  • Start small with cfg_select! – Replace a simple cfg-if block first to get comfortable with the macro syntax.
  • Use if-let guards to flatten nested patterns – They reduce indentation and improve readability in complex match statements.
  • Leverage the new atomic updates for lock-free code – They simplify compare-and-swap loops.
  • Consider the cold_path hint – Use it in rarely-taken branches to help the compiler optimize the hot path.
  • Stay on the nightly or beta channel if you want to test upcoming features early – just set rustup default beta and report any bugs you find.

Congratulations! You've now upgraded to Rust 1.95.0 and learned how to use its two major language features. Your code is now more concise and maintainable. Happy coding!