现状
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 层级放松先前普遍的假设,让类型选择退出。该工作放松了“所有类型都有编译时已知大小”以支持可扩展向量和外部类型。此目标将相同模式应用于“所有类型可以移动”和“所有类型可以忘记”。