Rust 新版解读 | 1.80 | “懒”类型、开区间范围模式

Rust 1.80 官方 release doc: Announcing Rust 1.80.0 | Rust Blog

通过 rustup 安装的同学可以使用以下命令升级到 1.80 版本:

$ rustup update stable

LazyCellLazyLock

这两个 "lazy" 类型会延迟数据的初始化直到第一次访问。它们类似于 1.70 版本中稳定的 OnceCellOnceLock 类型,但是初始化函数包含在 cell 中。这完成了吸纳流行的 lazy_staticonce_cell crates 中的功能到标准库中。

LazyLock 是线程安全的选项,适用于静态值的场景。例如,下面的例子中,无论是 spawn 线程还是主线程,都会看到相同的时间,因为 LAZY_TIME 只会被初始化一次,由哪个访问静态值的端点先访问。与 OnceLock::get_or_init() 不同,使用方都不需要知道如何初始化它。

use std::sync::LazyLock;
use std::time::Instant;

static LAZY_TIME: LazyLock<Instant> = LazyLock::new(Instant::now);

fn main() {
    let start = Instant::now();
    std::thread::scope(|s| {
        s.spawn(|| {
            println!("Thread lazy time is {:?}", LAZY_TIME.duration_since(start));
        });
        println!("Main lazy time is {:?}", LAZY_TIME.duration_since(start));
    });
}

LazyCell 也是一样的,只是没有线程同步,所以它不实现 Sync,但是它仍然可以在 thread_local! 静态值中使用(每个线程初始化一次)。取决于线程安全的需求如何,这两种类型都可以在其他数据结构中使用,来达到懒初始化的目的。

cfg 名称和值检查

在 1.79 版本中,rustc 稳定了一个 --check-cfg 标志,现在 Cargo 1.80 为所有的 cfg 名称和值启用了这些检查(除了 rustc 中已知的名称和值)。这包括来自 Cargo.toml 的特性名称以及来自构建脚本的新 cargo::rustc-check-cfg 输出。

出现期望外的 cfgs 会默认生成 unexpected_cfgs lint,用于捕获拼写错误或其他配置错误。例如,在一个有可选的 rayon 特性依赖项的项目中,这段代码配置了错误的特性值:

fn main() {
    println!("Hello, world!");

    #[cfg(feature = "crayon")]
    rayon::join(
        || println!("Hello, Thing One!"),
        || println!("Hello, Thing Two!"),
    );
}
warning: unexpected `cfg` condition value: `crayon`
 --> src/main.rs:4:11
  |
4 |     #[cfg(feature = "crayon")]
  |           ^^^^^^^^^^--------
  |                     |
  |                     help: there is a expected value with a similar name: `"rayon"`
  |
  = note: expected values for `feature` are: `rayon`
  = help: consider adding `crayon` as a feature in `Cargo.toml`
  = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
  = note: `#[warn(unexpected_cfgs)]` on by default

无论实际是否启用 rayon 特性,都会报告该警告。

Cargo.toml 文件中的 [lints] 部分可以用于扩展自定义 cfg 的已知名称和值列表。rustc 自动提供了警告中使用的语法

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(foo, values("bar"))'] }

你可以在之前的发布 nightly 时的博客文章中阅读更多关于这个特性的内容。

开区间的范围模式

Rust 的范围模式现在可以使用右开区间,写作 a..b..b,类似于 RangeRangeTo 表达式类型。例如,以下模式现在可以使用相同的常量作为一个模式的结束和下一个模式的开始:

#![allow(unused)]
fn main() {
pub fn size_prefix(n: u32) -> &'static str {
    const K: u32 = 10u32.pow(3);
    const M: u32 = 10u32.pow(6);
    const G: u32 = 10u32.pow(9);
    match n {
        ..K => "",
        K..M => "k",
        M..G => "M",
        G.. => "G",
    }
}
}

之前,范围模式中只允许包含的(a..=b..=b)或开放的(a..)范围,所以像这样的代码需要为边界使用一个不同的常量(如 K - 1)。

该特性作为一个不稳定的特性已经实现了很长时间,阻碍其稳定的考虑主要是它可能会增加模式匹配中的混乱程度,增加出现差一错误的机会。为此,穷尽性检查已经得到了增强,以更好地检测模式匹配中的空缺,新的 lint non_contiguous_range_endpointsoverlapping_range_endpoints 将有助于检测是否正确使用了开区间和闭区间。

Others

其它更新细节,和稳定的 API 列表,参考原Blog