Rust 资讯:Rust 1.80.0 版本发布,新增 LazyCell 和 LazyLock 类型,启用检查 cfg 名称和值、模式中的排他范围等

660 阅读6分钟

rust-course-banner.jpeg Rust 发布团队发布了最新版本 1.80.0,Rust 是一种强大的编程语言,使开发者能够构建可靠、高效的软件,秉承了 Rust 团队一贯的风格,1.80.0 版本在效率、代码安全性和灵活性方面均有大幅升级,使得 Rust 语言更加强大和易用,这次更新带来了哪些新特性和改进?我们一起来看一看:

注:关于 1.80.0 更详细升级信息可查看 Rust blog 原文以及 1.80.0 的详细发行公告 。也可以在 GitHub 上查看 1.80.0 的发行日志 。

如果已经安装了以前版本的 Rust,可以通过以下命令升级到 1.80.0 版本:

rustup update stable

如果还没有安装,可以从网站上的相应页面获取 rustup 安装。

(命令: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

如果想使用测试未来版本,可以考虑使用 beta 版本(rustup default beta) 或 nightly 版本  (rustup default nightly),这两个版本不太稳定,使用过程中可能遇到错误,如果遇到任何错误,可以发送报告。

rust 1.80.0.png

1.80.0 稳定版主要带来以下变化:

新增 LazyCell 和 LazyLock 两个同步类型

Rust 1.80 中新增加了 LazyCell 和 LazyLock 两个同步原语,这些“懒惰”类型会延迟数据的初始化,直到第一次访问时才进行初始化。它们与 1.70 中稳定的 OnceCell 和 OnceLock 类型类似,但初始化函数包含在 cell 中。这标志着从流行的 lazy_static 和 once_cell 库中引入的功能已经在标准库中稳定化了,所以后续你应该不用使用社区的lazy_static 和 once_cell 库了。

注:

OnceLock:是 Rust 中的一种类型,其作用是提供一种只能被初始化一次的锁。一旦锁被获取,它就不能再次被初始化。这对于一些需要全局可变状态,但不能重复初始化的情况很有用。

OnceCell:是 Rust 中的一种类型,这种类型的实例只能被赋值一次。一旦实例被赋值,就不能再改变其值。这种类型通常用于延迟初始化的全局变量。

LazyLock是线程安全的,它是线程安全时的选项,因此适用于像静态值static这样的场合。无论哪个线程首次访问静态值,都只会被初始化一次,所有线程都会看到相同的值。

例如,下面例子中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! 静态中使用(每个线程都有不同的初始化)。

注:总的来说,LazyLockLazyCellOnceLockOnceCell都是用于延迟初始化数据的工具,LazyLockLazyCell内置了的初始化,所以使用者不需要手动初始化它,而OnceLockOnceCell则需要手动初始化。

检查 cfg 名称和值

在Rust 1.79 中,rustc稳定了一个--check-cfg标志,现在 Rust1.80 的 Cargo 对其cfg知道的所有名称和值启用这些检查,包括对Cargo.toml以及cargo::rustc-check-cfg构建脚本的输出。

默认警告unexpected_cfgs lint 会报告意外的 cfg,用于捕获拼写错误或其他错误配置。例如,在具有可选rayon依赖项的项目中,代码配置了错误的feature值,会被检测出来发出警告,如下所示:

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功能是否启用,都会报告相同的警告。

[lints]清单中的表格还可Cargo.toml用于扩展自定义的已知名称和值的列表cfgrustc自动提供警告中使用的语法。

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

可以在之前的博客文章中阅读有关此功能的更多信息。

模式中的排他范围(Exclusive ranges in patterns)

Rust 的范围现在可以使用独占端点,写成a..b..b类似于RangeRangeTo表达式类型。例如,以下模式现在可以在一个模式的终点和下一个模式的起点使用相同的常量:

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)。

一直以来,Exclusive ranges 是一个不稳定的功能。Rust 团队表示,该功能稳定下来的阻碍因素在于它们可能会增加混乱并增加模式中出现 off-by-one errors 的可能性。在 Rust 1.80 中,exhaustiveness checking 得到了增强,可以更好地检测模式匹配中的差距,新的 lintnon_contiguous_range_endpoints 和 overlapping_range_endpoints 将有助于检测在哪些情况下需要将 exclusive 模式和 inclusive 模式相互切换。

稳定的 API

这些 API 现在在 const 上下文中是稳定的:

其他变化(Others)

了解更多有关 RustCargoClippy中所有变化的详细信息,请参阅完整的发布说明 ,其它更新细节,和稳定的API列表,以及贡献者列表。

官方资料

关于1.80.0 升级的更多信息,可以查阅: