An implementation of Joshua Yanovski's Ghost Cell paper.
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 4 年前 | ||
| 2 年前 | ||
| 5 年前 | ||
| 2 年前 | ||
| 5 年前 | ||
| 5 年前 | ||
| 4 年前 | ||
| 2 年前 |
GhostCell 论文中提出的一种新颖、安全且零成本的借用检查范式。
动机
许多数据结构(如链表、二叉树或 B 树)使用别名指针实现起来最为简便。
传统上,这意味着要使用运行时借用检查才能对这些结构进行修改,或者为了追求性能而使用 unsafe。
GhostCell 通过标记(brands) 将数据与其修改权限分离,并使用唯一的 GhostToken 来表示这种权限。该标记通过标记(brand) 在编译时与多个 GhostCell 相关联。
安全性
在 GhostCell 论文中,德国 MPI-SWS 的 Joshua Yanovski 及其同事利用他们在 RustBelt 项目 中开发的分离逻辑,正式证明了 GhostCell 的安全性。我个人对此深信不疑。
官方实现可在 https://gitlab.mpi-sws.org/FP/ghostcell/-/tree/master/ghostcell 找到,其中还包含示例。目前我已了解到该实现,不久后将对其进行升级。
使用风险自负!
(如有任何问题,请务必报告)
成熟度
这顶多算是一个 Alpha 质量的版本。
文档:
- 所有方法均有文档说明。
- 所有非平凡方法均提供示例。
测试:
- 所有非平凡方法均通过示例进行测试。
- 所有具有安全不变量的方法均配有编译失败测试。
- 包括示例在内的整个测试套件均在 Miri 下运行。
如何使用?
让我们从一个独立的示例开始:
use ghost_cell::{GhostToken, GhostCell};
fn demo(n: usize) {
let value = GhostToken::new(|mut token| {
let cell = GhostCell::new(42);
let vec: Vec<_> = (0..n).map(|_| &cell).collect();
*vec[n / 2].borrow_mut(&mut token) = 33;
*cell.borrow(&token)
});
assert_eq!(value, 33);
}
GhostToken 采用目前已知的最佳方式生成唯一生命周期,因此可将其用作一种标记(brand),具体方法是将以下两者结合:
- 在
GhostToken::new方法内部创建的局部变量。 - 一个必须对所有生命周期都有效的闭包。
这意味着存在两项限制:
- 闭包必须对生命周期具有协变性,这一点在闭包本身已包含引用时并不总能良好兼容。
- 闭包不能返回任何带有标记的项。
接下来,在闭包内部,任何 GhostCell 都可以且仅能与一个 GhostToken 相关联,该标记将对其借用权限进行编码:
&GhostToken<'brand>是使用GhostCell<'brand, T>::borrow的关键——注意此处匹配的'brand——并允许获取&T引用。&mut GhostToken<'brand>是使用GhostCell<'brand, T>::borrow_mut的关键,并允许获取&mut T引用。
使用 borrow 或 borrow_mut 会同时借用单元格和标记。
那又如何?
GhostCell 是一种安全且零成本的单元格。它支持通过编译时检查的借用规则实现别名。
与 StaticRc 结合使用,能够以安全、稳定的 Rust 编写双向链表、二叉树以及带有父指针的 B 树等数据结构。
其他单元格
目前还存在其他一些单元格,它们功能相似,但各有不同的取舍:
- 标准库中的
Cell和RefCell。 qcellcrate 中的多种单元格,其中LCell是基于与GhostCell作者的讨论而设计的,两者理念相似。
就这样吧!
感谢您的阅读。