Rust 1.64.0 发布

2022 年 9 月 22 日 · Rust 发布团队

Rust 团队很高兴地宣布 Rust 的新版本 1.64.0。Rust 是一种旨在赋能所有人的编程语言,用于构建可靠且高效的软件。

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

$ rustup update stable

如果您尚未安装,您可以从我们网站上的相应页面获取 rustup,并查看 GitHub 上 1.64.0 版本的详细发布说明

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

1.64.0 稳定版的新特性

使用 IntoFuture 增强 .await

Rust 1.64 稳定化了 IntoFuture trait。IntoFuture 是一个类似于 IntoIterator 的 trait,但 IntoFuture 不是支持 for ... in ... 循环,而是改变了 .await 的工作方式。使用 IntoFuture.await 关键字可以等待的不仅仅是 future;它可以等待任何可以通过 IntoFuture 转换为 Future 的东西——这可以帮助使您的 API 更加用户友好!

例如,考虑一个构建器,它构建对某些网络存储提供商的请求

pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Create a new instance of `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Decide whether debug mode should be enabled.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Send the request and receive a response.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

典型的用法可能如下所示

let response = StorageRequest::new()  // 1. create a new instance
    .set_debug(true)                  // 2. set some option
    .send()                           // 3. construct the future
    .await?;                          // 4. run the future + propagate errors

这还不错,但我们可以做得更好。使用 IntoFuture,我们可以将“构建 future”(第 3 行)和“运行 future”(第 4 行)合并为一个步骤

let response = StorageRequest::new()  // 1. create a new instance
    .set_debug(true)                  // 2. set some option
    .await?;                          // 3. construct + run the future + propagate errors

我们可以通过为 StorageRequest 实现 IntoFuture 来做到这一点。IntoFuture 要求我们有一个可以返回的具名 future,我们可以通过创建一个“boxed future”并为其定义一个类型别名来实现

// First we must import some new types into the scope.
use std::pin::Pin;
use std::future::{Future, IntoFuture};

pub struct Error { ... }
pub struct StorageResponse { ... }
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Create a new instance of `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Decide whether debug mode should be enabled.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Send the request and receive a response.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

// The new implementations:
// 1. create a new named future type
// 2. implement `IntoFuture` for `StorageRequest`
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error>> + Send + 'static>>
impl IntoFuture for StorageRequest {
    type IntoFuture = StorageRequestFuture;
    type Output = <StorageRequestFuture as Future>::Output;
    fn into_future(self) -> Self::IntoFuture {
        Box::pin(self.send())
    }
}

这需要更多代码来实现,但为用户提供了更简单的 API。

未来,Rust Async WG 希望通过支持 type 别名中的 impl Trait(类型别名 Impl Trait 或 TAIT)来简化创建新的具名 future。这应该通过简化类型别名的签名来使实现 IntoFuture 更容易,并通过从类型别名中删除 Box 使其性能更高。

core 和 alloc 中与 C 兼容的 FFI 类型

当调用 C ABI 或被 C ABI 调用时,Rust 代码可以使用诸如 c_uintc_ulong 之类的类型别名来匹配任何目标平台上 C 的相应类型,而无需特定于目标的代码或条件。

以前,这些类型别名仅在 std 中可用,因此为嵌入式目标和其他只能使用 corealloc 的场景编写的代码无法使用这些类型。

Rust 1.64 现在在 core::ffi 中提供了所有 c_* 类型别名,以及用于处理 C 字符串的 core::ffi::CStr。Rust 1.64 还提供了 alloc::ffi::CString,用于仅使用 alloc crate 而不是完整的 std 库来处理拥有的 C 字符串。

rust-analyzer 现在可通过 rustup 获取

rust-analyzer 现在作为 Rust 附带的工具集合的一部分包含在内。这使得下载和访问 rust-analyzer 更加容易,并使其在更多平台上可用。它作为一个 rustup 组件提供,可以使用以下命令安装

rustup component add rust-analyzer

目前,要运行 rustup 安装的版本,您需要通过以下方式调用它

rustup run stable rust-analyzer

下一个 rustup 版本将提供一个内置代理,以便运行可执行文件 rust-analyzer 将启动相应的版本。

大多数用户应继续使用 rust-analyzer 团队提供的版本(可在 rust-analyzer 发布页面 上获得),这些版本发布频率更高。官方 VSCode 扩展的用户不受影响,因为它会在后台自动下载和更新版本。

Cargo 改进:工作区继承和多目标构建

当在一个 Cargo 工作区中使用相关的库或二进制 crate 集合时,您现在可以避免 crate 之间重复的公共字段值,例如公共版本号、存储库 URL 或 rust-version。这也有助于在更新这些值时保持 crate 之间的同步。有关更多详细信息,请参阅 workspace.packageworkspace.dependencies“从工作区继承依赖项”

在为多个目标构建时,您现在可以将多个 --target 选项传递给 cargo build,以一次构建所有这些目标。您还可以将 build.target 设置为 .cargo/config.toml 中的多个目标数组,以便默认情况下为多个目标构建。

稳定的 API

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

这些类型以前在 std::ffi 中是稳定的,但现在在 corealloc 中也可用:

这些类型以前在 std::os::raw 中是稳定的,但现在在 core::ffistd::ffi 中也可用:

我们已经稳定了一些用于 Poll 的助手,Poll 是 futures 底层的低级实现:

未来,我们希望提供更简单的 API,减少对诸如 PollPin 之类的低级细节的使用,但在过渡时期,这些助手使编写此类代码更容易。

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

兼容性说明

  • 正如先前宣布的那样,linux 目标现在至少需要 Linux kernel 3.2(除了已经需要更新内核的目标),并且 linux-gnu 目标现在需要 glibc 2.17(除了已经需要更新 glibc 的目标)。

  • Rust 1.64.0 更改了 Ipv4AddrIpv6AddrSocketAddrV4SocketAddrV6 的内存布局,使其更紧凑和内存高效。这种内部表示从未公开,但一些 crate 仍然通过使用 std::mem::transmute 来依赖它,从而导致无效的内存访问。标准库的此类内部实现细节永远不会被视为稳定的接口。为了限制损害,我们与所有仍在维护的 crate 的作者合作发布了修复版本,这些版本已经发布一年多了。绝大多数受影响的用户应该能够通过 cargo update 来缓解。

  • 作为 RLS 弃用的一部分,这也是包含 RLS 副本的最后一个版本。从 Rust 1.65.0 开始,RLS 将被一个显示弃用警告的小型 LSP 服务器取代。

其他更改

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

  • Rust 编译器的 Windows 构建现在使用 profile-guided optimization,为在 Windows 上编译 Rust 代码提供 10-20% 的性能提升。

  • 如果您定义一个包含从未使用过的字段的结构体,rustc 将警告未使用的字段。现在,在 Rust 1.64 中,您可以启用 unused_tuple_struct_fields lint,以获得关于元组结构体中未使用字段的相同警告。在未来的版本中,我们计划默认启用此 lint 警告。单元类型 (()) 的字段不会产生此警告,以便更容易迁移现有代码,而无需更改元组索引。

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

1.64.0 的贡献者

许多人共同创建了 Rust 1.64.0。没有你们所有人,我们不可能做到。谢谢!