27873
Technology

Exploring Rust 1.95.0: New Macros, Match Guards, and Stabilized APIs

Rust 1.95.0 has arrived, bringing a fresh set of tools and improvements to enhance your coding experience. This release introduces a powerful compile-time macro, refines pattern matching with if-let guards, and stabilizes a wide range of APIs for safer and more efficient programming. Whether you're a seasoned Rustacean or just getting started, this update offers something valuable. Below, we answer key questions about the new features and how to get started.

How can I update my Rust installation to version 1.95.0?

If you already have Rust installed via rustup, updating is straightforward. Simply open your terminal and run:

Exploring Rust 1.95.0: New Macros, Match Guards, and Stabilized APIs
Source: blog.rust-lang.org
$ rustup update stable

This command fetches the latest stable release (1.95.0) and applies it to your environment. If you don't have rustup yet, head to the official Rust website to download it. For those eager to test upcoming features, you can switch to the beta or nightly channels with rustup default beta or rustup default nightly. If you encounter any issues, please report them on the Rust issue tracker to help improve future releases.

What is the new cfg_select! macro and how does it work?

The cfg_select! macro, stabilized in Rust 1.95.0, acts as a compile-time conditional selector based on configuration predicates. It works similarly to the popular cfg-if crate but with its own syntax. You provide multiple arms, each with a condition and a block of code. The macro expands to the right-hand side of the first arm whose predicate evaluates to true. For example:

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

This macro is especially useful for writing platform-specific code without external dependencies, keeping your build simple and fast.

How do if-let guards enhance pattern matching in Rust 1.95.0?

Building on the let chains stabilized in Rust 1.88, version 1.95.0 extends if-let guards to match expressions. Now you can combine pattern matching with additional conditions inside a match arm. Consider this example:

match value {
    Some(x) if let Ok(y) = compute(x) => {
        println!("{}, {}", x, y);
    }
    _ => {}
}

Here, the arm matches only if value is Some(x) and the result of compute(x) is Ok(y). Both x and y become available inside the block. Note that the compiler currently does not consider these patterns in exhaustiveness checks, similar to traditional if guards. This addition makes match expressions more expressive and reduces the need for nested if let constructs.

What are the key stabilized APIs in Rust 1.95.0?

This release stabilizes a broad set of APIs, focusing on memory safety, concurrency, and ergonomics. Notable additions include:

  • MaybeUninit<[T; N]> trait implementations: New From, AsRef, and AsMut conversions simplify working with uninitialized arrays.
  • Cell array trait implementations: Cell<[T; N]> and Cell<[T]> now implement AsRef for easier access.
  • Boolean conversion: bool: TryFrom<{integer}> allows safe conversion from integers.
  • Atomic operations: AtomicPtr::update, AtomicBool::update, and their try_update variants provide a convenient way to atomically modify values without a full fetch-and-loop pattern.
  • New module and functions: core::range introduces RangeInclusive and its iterator; core::hint::cold_path marks unlikely code paths; as_ref_unchecked and as_mut_unchecked for raw pointers enable unsafe but efficient access.
  • Collection mutability helpers: Methods like Vec::push_mut and VecDeque::push_front_mut simplify inserting mutable references into collections.

What is the new core::range module about?

Stabilized in Rust 1.95.0, the core::range module provides abstractions for numeric ranges without relying on iterators. It includes RangeInclusive (a range that includes both endpoints) and its dedicated iterator type RangeInclusiveIter. This module is part of the standard library's core, meaning it's available in no_std environments as well. It offers a more explicit way to represent and iterate over intervals, improving code clarity and enabling optimizations. For instance, you can create a closed range 0..=5 and iterate over it with the new iterator, or use the range type in generic contexts where you need bounded numeric intervals.

How do the new atomic update and try_update functions work?

Rust 1.95.0 adds update and try_update methods to AtomicPtr, AtomicBool, and atomic integers. These methods allow you to atomically modify the stored value without manually writing a compare-and-exchange loop. The update function takes a closure that receives a mutable reference to the current value and returns the new value. If the value changes concurrently, the operation retries. For example:

use std::sync::atomic::{AtomicBool, Ordering};
let flag = AtomicBool::new(false);
flag.update(|val| !val, Ordering::SeqCst);

The try_update variant returns a Result, succeeding only if no other thread modified the value during the operation. These additions make concurrent programming more ergonomic and reduce boilerplate.

💬 Comments ↑ Share ☆ Save