LWN:Rust 的近期重要改动!

关注了就能看到更多这么棒的文章哦~

Some recent and notable changes to Rust

December 11, 2023
This article was contributed by Daroc Alden
Gemini translation
https://lwn.net/Articles/954033/

Rust 项目每六周进行一次增量发布,这个做法很容易让人忽略该语言即将发生的一些值得关注的改动,例如新的 ABI、更好的调试器支持、异步特性以及对 C 字符串的支持。年底提供了回顾过去几个月中更新的机会,并且展望 2024 年。

stack unwinding across foreign function interfaces

Rust 允许与用其他语言编写的程序进行链接。这既包括允许 Rust 程序使用具有兼容 C 接口的现有库,也允许在现有项目中无缝使用 Rust 库。这种跨语言的兼容性是允许在大型程序中逐步采用 Rust 组件的关键部分。从而将 Lua 和 SQLite 等复杂库嵌入到 Rust 程序中成为可能。

目前,Rust 中的 panic 或来自另一种语言(例如 C++)的异常,会试图跨越语言的边界来展开堆栈,无论是展开哪个方向的调用都是未定义的行为。实际上,像这样的多语言程序目前运行良好,需要各种对象文件都使用类似 -fexceptions 标志进行编译,以告诉编译器即使在不需要它的语言(例如 C)中也要使用展开(unwinding)支持来编译函数。但是,由于展开功能不是 ABI 的正式部分,因此如果两侧的编译器更改了格式化展开信息的方式,这是随时可能发生的。

异常(exception)也不是 unwind 的唯一用途。Linux 上的 GNU C 库 (glibc) 使用“强制展开”来实现 pthread_exit() 和 pthread_cancel() 。与 Linux 不同,glibc 通过在 setjmp() 中保存足够的堆栈位置信息来实现 longjmp() ,而无需堆栈展开,Windows 也使用强制展开来实现 longjmp() 。跨语言展开的限制使得在使用这些机制的程序之外编写跨平台的 wrapper 并且不触发未定义行为就变成不可能的任务了。

FFI-unwind 工作组希望通过指定如何让堆栈展开可以跨越语言之间的边界来改变这一点。

在 2023 年 7 月发布的 Rust 1.71.0 中,FFI-unwind 工作组通过对外部函数的现有 ABI 定义中添加 -unwind 变体,迈出了明确定义行为的第一步。Rust 中的程序可以指定应为哪些 ABI 特定的外部函数(或程序期望链接的库中的函数)进行编译,包括“C”、“stdcall”或“fastcall”等选项。新的 -unwind 变体现在行为完全可以确定,但将在 FFI-unwind 工作组完成其任务时可能会改变成新的行为,确保行为一致。程序员如果希望编写面向未来的库代码,那么现在就可以开始使用 -unwind ABI 说明符:

// C function called from Rust
#[link(name = "c_library")]
extern "C-unwind" {
    fn function_to_link_against();
}
// Rust function callable from C
#[no_mangle]
pub extern "C-unwind" fn callable_from_c() {
    // Function body here
}

嵌入式调试器可视化信息(Embedded debugger visualization information)

Rust 1.71.0 还包括对将 GDB 可视化脚本或 Windows Natvis 描述直接嵌入 Rust 库的支持。这些脚本允许连接到 GDB 或 Natvis 的打印代码,以便为库定义的数据类型提供自定义可视化效果。标准库已经附带了针对核心数据类型的调试器可视化脚本,但这个改动就使该功能也可以让供用户代码使用了。

由于 GDB 不会自动从正在调试的二进制文件中加载脚本,因此必须将包含有问题的 Rust 库的对象文件添加到 GDB 的自动加载安全路径中,以便从包含的可视化脚本中受益。

Improvements to thread-local initialization

2023 年 10 月发布的 Rust 1.73.0 引入了让 thread-local 内容初始化变得更加容易的支持。Rust 中的线程局部值是由称为 LocalKey 的数据类型所表示的。过去,访问存储在 LocalKey 中的值需要使用回调函数。现在,存储着内部可变性内容的 LocalKey 对象支持 get() 、 set() 、 take() 和 replace() 方法来直接修改或检索包含的值。

Captured lifetimes in opaque types and asynchronous trait methods

能够编写异步特性方法,这是一项人们经常提出的功能,因为它可以减少异步程序中多个组件之间的耦合。例如,它允许定义异步析构函数,从而简化异步程序中的资源管理动作。这是今年异步工作组的主要目标。作为实现这一目标的持续工作的一部分,2023 年 11 月发布的 Rust 1.74.0 解决了一个长期存在的问题,也就是阻止具有不透明返回类型的特性方法(trait method)隐式地获取生命周期信息。这是允许异步特性方法的最后一道障碍。

多年来,一直有库(例如 async_trait)可以解决此问题,从而在这些年里支持异步特征方法,但人们认为这些库不适合被包含在标准库中,因为它们依赖于引入的额外的动态分派层。Rust 项目表示,“抽象需要没有额外开销”是 Rust 设计的关键支柱,包括要保证在编译时能知道类型的情况下可以静态分派 trait。

要了解为何 fix 这个问题对于实现最终目标是有必要的,可能需要先定义一些术语。这里问题是,具有不透明返回类型 的 trait 方法无法隐式地捕获生存期信息。捕获生存期信息(capturing lifetime information)是指创建一种类型,使得该类型的有效性在某种程度上取决于生存期信息。当类型包含对存在于其他位置的借用数据(borrowed data)的引用时,它就需要信息来指定引用的数据会在多长时间内有效,以便 Rust 编译器可以执行其静态生存期分析并保证引用的数据比返回的类型使用的时间更长。

例如,有如下定义的变量:

Wrapper<'a>

其中包含如下字段:

&'a Foo

必须比它捕获的生存期( 'a )更短,从而可以静态地保证不会发生任何使用后释放(use-after-free)的情况。

在 Rust 之前版本中,会接受显式生命周期信息,例如在 trait 声明中给出的生命周期绑定(lifetime bound)。然而,当 Rust 编译异步函数时,编译器会使其返回一个实现 Future 特征(trait)的匿名结构,也是编译器生成的结构。此结构可以捕获在方法体中定义的变量的生命周期信息,这些信息不存在于 trait 中方法的声明中,因此在返回类型中没有显式命名。

不透明返回类型(opaque return type, 有时也称为 -> impl Trait )是指 Rust 针对编写返回实现接口的某些具体类型的函数或方法,而不需要说明该类型是什么的这种需求的一个实现。这包括用一个匿名结构来代表一个异步函数。具有不透明返回类型的 trait 方法可以让编译器直接返回匿名结构,而无需将其封装在具有 vtable 的 heap 分配中,这是现有的库所采用的方法。

在 Rust 1.73.0 中,可以编写一个返回不带任何封装的不透明类型的普通函数,但无法将相同的内容写成一个 trait 方法。现在,这个先决条件已完成,Rust 1.75.0(即将发布,预计在 12 月底发布)将包括对在 trait 中声明异步方法的支持,其语法如下所示:

trait Trait {
    async fn method(self) -> usize;
}
impl Trait for Foo {
    async fn method(self) -> usize {
  // Function body here
    }
}

这相当于将特征方法定义为返回 impl Future<Output = usize> 。

随着这项工作完成,异步工作组打算在 2024 年添加对异步迭代器(asynchronous iterator)的支持,并在 2027 年添加对异步析构(asynchronous destructor)函数的支持。这也解锁了关键字泛型(keyword generics)相关设计工作,这个工作的目的是编写库代码来抽象出 I/O 操作是否要异步完成。

Other upcoming changes

Rust 1.75.0 还将包含来自已批准的 RISC-V 扩展的其他固有函数。其中包括原子指令和标量密码指令。

Rust 1.75.0 将是最后一个支持 Windows 7、8 和 8.1 的版本。从 2 月份的 Rust 1.76.0 开始,只有 Windows 10 及更高版本才受支持,并且作为一级目标,也就是官方发布二进制版本可供使用,并且已完成了自动化测试以确保它们保持稳定。更古老的 Windows 版本将保留为“旧版”目标,但不会保证能获取到支持。

Rust 1.76.0 还将带来对 C 语言里使用的以空值结尾的字符串(string literals)的支持,以减少编写与 C 通信的代码的痛苦。与仅包含标准化 UTF-8 数据的普通 Rust 字符串不同,这些字符串将能够包含 UTF-8 以外编码的数据,就像 byte 方式的字符串一样。新的字符串将自动添加空值终止符。它们的写法是 c"…" ,并为 Rust 生态系统中存在的各种 C 字符串宏提供了一个替代方案。

Rust 2024 Edition

Rust 的下一个大版本(Edition)计划在 2024 年发布。Edition 是 Rust 允许向后不兼容的语法更改而不破坏语言的解决方案。用不同 Edition 的 Rust 编写的 crate 仍然可以互操作,但新的 Edition 里允许 crate 自己决定是否加入新功能。

目前尚不清楚 2024 Edition 还将包含哪些内容,或者 2024 年版本在明年的确切发布时间。然而,Rust 项目对 2024 年 edition 提出的目标包括继续关注开发人员的易用性上,尤其是在编写异步代码方面。我们已经知道该版本预计将包含用于创建生成器的全新语法,以及 Future 和 IntoFuture 特征成为标准库前奏的一部分(所有 Rust 程序都可以使用的函数、类型和特征的集合,无需 import)。

自 2015 年 1.0 版本发布以来,Rust 已经取得了长足的进步,但它仍在不断积累新的令人兴奋的功能。虽然对异步代码的改进几乎肯定会成为 2024 年 edition 的重要功能,但还有许多较小的功能也有助于该语言的持续发展。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值