ESC
开源 1 分钟阅读

Rust项目目标:不可移动类型与保证析构器

Rust项目提出新目标,引入Move和Forget trait,允许类型选择不可移动和必须运行析构器。这解决了自引用类型复杂性和保证析构器运行的问题,解锁如安全作用域生成等新模式,对Rust生态系统有重要影响。

来源:Hacker News

现状

Rust 历史上假设所有值都可以移动(在内存中重新定位)并通过 mem::forget 忘记(不运行析构器)。这些假设深植于语言中:赋值移动值,且 mem::forget 是安全的。但某些类型需要选择退出这些能力:

不可移动类型: 许多异步未来需要自引用,但自引用类型无法安全移动。当前解决方案是 Pin,它将不可移动性编码为位置而非类型的属性。这导致了显著的复杂性。正如安全 Pin 初始化问题所述,Pin 在 Linux 内核等系统中难以安全编码自引用类型。

保证析构器: 某些类型需要其析构器运行。Transaction 类型可能需要在清理前调用 commit() 或 rollback()。有作用域的任务句柄必须在作用域退出前 join。但 mem::forget 是安全的,所以 Rust 无法保证析构器运行。这阻塞了如安全作用域异步生成等模式,其中生成的任务借用父作用域。

我们建议通过新的 auto-trait 泛化 Rust 的类型系统,描述类型上可能的操作。框架是积极的:trait 表示能力。在基础层,类型可能没有特殊能力。然后我们叠加需要的东西:

这遵循了Sized 层级工作建立的先例。正如该工作放松了“所有类型都有编译时已知大小”以支持可扩展向量,这项工作放松了“所有类型可以移动”和“所有类型可以忘记”。

Move trait 将可移动性编码为类型而非位置的属性:

#[lang = “move”] unsafe auto trait Move {}

实现 !Move 的类型不能移动,必须在整个存在期间保持稳定地址。这比 Pin 更简单,因为不可移动性是类型属性,而非位置属性。构造 !Move 类型将依赖于#t-lang/in-place-init的工作。

Forget trait 允许类型选择退出可忘记性:

// 实现 !Forget 的类型必须运行其析构器 unsafe impl !Forget for ScopedTaskHandle {}

使用 !Forget,我们可以构建安全作用域生成:句柄的析构器 join 任务,且因为句柄不能被忘记,join 是保证的。这解锁了当前在安全 Rust 中不可能的模式。

Move trait

让类型选择退出内存中重新定位,将不可移动性编码为类型而非位置的属性。

探索让类型选择退出 mem::forget,启用如安全作用域异步生成等模式。

具体超出今年范围的是任何与更改或更新 Future trait 相关的内容。这是 Rust 中唯一依赖于 Pin 的稳定 trait,需要迁移故事才能使用 Move。然而,依赖于 Pin 并不是 Future 的唯一缺点(1 + 2 + 更多10个问题),因此修复 Future trait 最好作为独立项目处理。

团队支持级别备注
[lang]大需要设计会议进行设计
[types]大参与实现 + 审查

常见问题

这与 Sized 层级工作有何关系?

Sized 层级工作建立了模式:Rust 可以通过引入 trait 层级放松先前普遍的假设,让类型选择退出。该工作放松了“所有类型都有编译时已知大小”以支持可扩展向量和外部类型。此目标将相同模式应用于“所有类型可以移动”和“所有类型可以忘记”。