Rust 新版解读 | 1.64 | 重点: IntoFuture , Cargo 优化

Rust 1.64 官方 release doc: Announcing Rust 1.64.0 | Rust Blog

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

$ rustup update stable

使用 IntoFuture 增强 .await

1.64 稳定了 IntoFuture trait,不同于用在 for ... in ...IntoIterator trait,IntoFuture 增强了 .awiat 关键字。现在 .await 可以 await 除了 futures 外,还可以 await 任何实现了 IntoFuture trait 并经此转换成 Future 的对象。这可以让你的 api 对用户更加优化。

举一个用在网络存储供应端的例子:

#![allow(unused)]
fn main() {
pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);

impl StorageRequest {
    /// 实例化一个 `StorageRequest` 
    pub fn new() -> Self { ... }
    /// 是否开启 debug 模式
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// 发送请求并接受回复
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}
}

通常地使用方法可能类似如下代码:

#![allow(unused)]
fn main() {
let response = StorageRequest::new()  // 1. 实例化
    .set_debug(true)                  // 2. 设置一些选项
    .send()                           // 3. 构造 future
    .await?;                          // 4. 执行 future ,传递 error
}

这个代码已经不错了,不过 1.64 后可以做的更好。使用 IntoFuture ,把第三步的 “构造 future ” 和 第四步的 “执行 future ” 合并到一个步骤里:

let response = StorageRequest::new()  // 1. 实例化
    .set_debug(true)                  // 2. 设置一些选项
    .await?;                          // 3. 构造并执行 future ,传递 error

想要实现上面的效果,我们需要给 StorageRequest 实现 IntoFuture trait。IntoFuture 需要确定好要返回的 future,可以用下面的代码来实现:

#![allow(unused)]
fn main() {
// 首先需要引入一些必须的类型
use std::pin::Pin;
use std::future::{Future, IntoFuture};

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

impl StorageRequest {
    /// 实例化一个 `StorageRequest` 
    pub fn new() -> Self { ... }
    /// 是否开启 debug 模式
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// 发送请求并接受回复
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

// 新的实现内容
// 1. 定义好返回的 future 类型
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error>> + Send + 'static>>
// 2. 给 `StorageRequest` 实现 `IntoFuture`
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 异步团队 希望能够通过给类型别名提供 impl Trait Type Alias Impl Trait,来简化定义 futures 实现 IntoFuture 的代码;再想办法移除 Box 来提升性能。

corealloc 中和 C 语言兼容的 FFI 类型

当调用 C-ABI 或者调用 C-ABI 的时候,Rust 代码通常会使用诸如 c_uint 或者 c_ulong 的类型别名来匹配目标语言里的对应类型。

在次之前,这些类型别名仅在 std 里可用,而在嵌入式或者其它仅能使用 core 或者 alloc 的场景下无法使用。

1.64 里在 core::ffi 里提供了所有 c_* 的类型别名,还有 core::ffi::CStr 对应 C 的字符串,还有仅用 alloc 库情况下可以用 alloc::ffi::CString 来对应 C 的字符串。

可以通过 rustup 来使用 rust-analyzer

rust-analyzer 现在被加进 Rust 工具集里了。这让在各平台上下载使用 rust-analyzer 更加方便。通过 rustup component 来安装:

rustup component add rust-analyzer

目前,使用 rustup 安装的版本,需要这样启用:

rustup run stable rust-analyzer

下一次 rustup 的发布本把会提供一个内置的代理,来运行对应版本的 rust-analyzer 。

Cargo 优化,workspace 继承和多目标构建

当在一个 Cargo workspace 里管理多个相关的库/产品时,现在可以避免在多个库里使用相同的字段值了,比如相同的版本号,仓库链接,rust-version。在更新的时候也可以更容易地保持这些信息地一致性。更多细节可以参考:

另外在构建多个目标地时候,现在可以直接传递多个 --target 选项给 cargo build 来一次性编译所有目标。也可以在 .cargo/config.toml 里设置一个 build.target 的 array 来改变默认构建时的对象。

稳定API && Others

更多稳定API列表和其它更新内容,请参考原文最后 stabilized-apis