Rust 1.65.0 发布

2022年11月3日 · Rust 发布团队

Rust 团队很高兴地宣布 Rust 的新版本 1.65.0。Rust 是一种编程语言,它使每个人都能构建可靠高效的软件。


在详细介绍新的 Rust 版本之前,我们想提请大家注意 Mahsa Amini 的悲惨死亡,以及伊朗宗教道德警察对许多其他人的死亡和暴力镇压。有关更多详细信息,请参阅 https://en.wikipedia.org/wiki/Mahsa_Amini_protests。我们与伊朗人民争取人权的斗争站在一起。


如果您之前通过 rustup 安装了 Rust 版本,可以使用以下命令获取 1.65.0:

$ rustup update stable

如果您还没有安装,您可以从我们网站上的相应页面获取 rustup,并查看 GitHub 上的1.65.0 的详细发行说明

如果您想通过测试未来版本来帮助我们,您可以考虑在本地更新以使用 beta 通道 (rustup default beta) 或 nightly 通道 (rustup default nightly)。请报告您可能遇到的任何错误!

1.65.0 稳定版中的新特性

泛型关联类型 (GAT)

现在可以在关联类型上定义生命周期、类型和常量泛型,如下所示:

trait Foo {
    type Bar<'x>;
}

很难用几句话来概括它们的用途,所以这里有一些示例 trait,以了解它们的功能:

/// An `Iterator`-like trait that can borrow from `Self`
trait LendingIterator {
    type Item<'a> where Self: 'a;

    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

/// Can be implemented over smart pointers, like `Rc` or `Arc`,
/// in order to allow being generic over the pointer type
trait PointerFamily {
    type Pointer<T>: Deref<Target = T>;

    fn new<T>(value: T) -> Self::Pointer<T>;
}

/// Allows borrowing an array of items. Useful for
/// `NdArray`-like types that don't necessarily store
/// data contiguously.
trait BorrowArray<T> {
    type Array<'x, const N: usize> where Self: 'x;

    fn borrow_array<'a, const N: usize>(&'a self) -> Self::Array<'a, N>;
}

正如您所看到的,GAT 非常通用,并且可以启用许多当前无法编写的模式。有关更多信息,请查看去年发布的推动稳定化的帖子或上周发布的稳定化公告帖子。前者更深入地探讨了上面的一些示例,而后者则讨论了此稳定化的一些已知限制。

您可以在nightly 参考原始 RFC(最初在 6.5 年前提出!)中找到更深入的阅读材料。

let-else 语句

这引入了一种新的 let 语句类型,它具有一个可反驳的模式和一个当该模式不匹配时执行的发散 else 块。

let PATTERN: TYPE = EXPRESSION else {
    DIVERGING_CODE;
};

正常的 let 语句只能使用不可反驳的模式,这些模式在静态上已知始终匹配。该模式通常只是一个单一的变量绑定,但也可能解包复合类型,如结构体、元组和数组。然而,这对于条件匹配是不可用的,比如提取枚举的变体 -- 直到现在!使用 let-else,一个可反驳的模式可以像普通的 let 一样在周围的作用域中匹配和绑定变量,否则当模式不匹配时会发散(例如,breakreturnpanic!)。

fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

名称绑定的作用域是使它与 matchif let-else 表达式不同的主要因素。您之前可以使用不幸的重复和外部 let 来近似这些模式:

    let (count_str, item) = match (it.next(), it.next()) {
        (Some(count_str), Some(item)) => (count_str, item),
        _ => panic!("Can't segment count item pair: '{s}'"),
    };
    let count = if let Ok(count) = u64::from_str(count_str) {
        count
    } else {
        panic!("Can't parse integer: '{count_str}'");
    };

从带标签的块中 break

现在可以将普通块表达式标记为 break 目标,提前终止该块。这听起来有点像 goto 语句,但它不是任意跳转,只能从块内跳转到其末尾。这对于 loop 块已经可以实现,您可能已经看到人们编写只执行一次的循环,只是为了获得带标签的 break

现在有一个专门针对此的语言特性!带标签的 break 也可以包括一个表达式值,就像循环一样,允许一个多语句块具有提前的“返回值”。

let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};

拆分 Linux 调试信息

早在 Rust 1.51 中,编译器团队就添加了对 macOS 上拆分调试信息的支持,现在此选项也稳定可用于 Linux。

  • -Csplit-debuginfo=unpacked 会将调试信息拆分到多个 .dwo DWARF 对象文件中。
  • -Csplit-debuginfo=packed 会在输出二进制文件的旁边生成一个包含所有打包在一起的调试信息的 .dwp DWARF 包。
  • -Csplit-debuginfo=off 仍然是默认行为,它将 DWARF 数据包含在对象和最终二进制文件的 .debug_* ELF 部分中。

拆分 DWARF 使链接器避免处理调试信息(因为它不再位于被链接的对象文件中),这可以加快链接时间!

其他目标现在也接受 -Csplit-debuginfo 作为具有平台特定默认值的稳定选项,但指定其他值仍然不稳定。

稳定的 API

以下方法和 trait 实现现在已稳定:

特别值得注意的是,Backtrace API 允许随时捕获堆栈回溯,使用通常为 panic 回溯提供服务的相同平台特定实现。例如,这对于向错误类型添加运行时上下文可能很有用。

这些 API 现在可在 const 上下文中使用:

兼容性说明

  • 作为 RLS 弃用的最后一步,此版本已将 RLS 替换为一个小型 LSP 服务器,该服务器会显示弃用警告,建议用户迁移到 rust-analyzer

其他更改

Rust 1.65 版本中还有其他更改,包括:

  • 现在针对优化的编译启用了 MIR 内联。这为真实世界的 crate 提供了 3-10% 的编译时间改进。
  • 在调度构建时,Cargo 现在会对待处理作业的队列进行排序,以提高性能。

查看 RustCargoClippy 中更改的所有内容。

1.65.0 的贡献者

很多人共同创建了 Rust 1.65.0。没有大家,我们就无法做到这一点。谢谢!