4个版本 (2个重大更新)
0.3.1 | 2022年3月12日 |
---|---|
0.3.0 | 2021年12月12日 |
0.2.0 | 2021年12月7日 |
0.1.0 | 2021年9月9日 |
#1900 在 数据结构
30KB
497 行
erased-type-arena
一个带有适当释放操作的类型擦除分配区域。它与 typed-arena
类似,但是泛型类型参数从区域中被擦除,并且区域能够分配不同类型的值。此外,通过动态检查防止了由于 drop
函数实现不当而导致的潜在使用后释放漏洞。
动机
在100%安全的Rust中实现类似于图的数据结构并不容易,因为图节点可能被多个节点共享,这本质上违反了Rust的所有权规则。克服这一问题的典型方法是在 分配区域 中分配图节点,并且每个节点通过指向内部可变容器的不可变引用(如 RefCell
)被多个其他节点共享。我们可以通过以下代码定义来说明这种方法
struct GraphContext {
node_arena: Arena,
}
impl GraphContext {
fn alloc_node<'ctx>(&'ctx self) -> &'ctx RefCell<GraphNode<'ctx>> {
self.node_arena.alloc(RefCell::new(GraphNode {
other_nodes: Vec::new(),
}))
}
}
struct GraphNode<'ctx> {
other_nodes: Vec<&'ctx RefCell<GraphNode<'ctx>>>,
}
我们可以选择由 bumpalo
包提供的区域分配器作为我们的节点分配区域。在大多数情况下,这都可以正常工作。然而,如果图节点实现了 Drop
特性,由于 bumpalo
提供的区域分配器不支持在区域本身被释放时执行释放函数,因此它没有其他选择。
typed-arena
是另一个提供分配区的 crate,当分配区本身被丢弃时,它会适当地丢弃分配的值。然而,typed-arena
提供的分配区的类型需要一个泛型类型参数,以指示分配区可以分配的值的类型。这个细微的差异使得它在我们的图结构示例中变得不可行,因为现在 GraphContext
的生命周期注解将传播到自身
struct GraphContext<'ctx> { // The `'ctx` lifetime notation here is clearly inappropriate
node_arena: Arena<RefCell<GraphContext<'ctx>>>,
}
impl GraphContext {
fn alloc_node<'ctx>(&'ctx self) -> &'ctx RefCell<GraphNode<'ctx>> {
self.node_arena.alloc(RefCell::new(GraphNode {
other_nodes: Vec::new(),
}))
}
}
struct GraphNode<'ctx> {
other_nodes: Vec<&'ctx RefCell<GraphNode<'ctx>>>,
}
为了克服上述分配区的限制,这个 crate 提供了一个分配区,
- 当分配区本身被丢弃时,适当地丢弃分配的值,就像
typed-arena
所做的那样; - 分配区可以分配不同类型的值,并且泛型类型参数从分配区的类型中擦除。相反,泛型类型参数被移动到
alloc
函数。
丢弃安全性
如果分配值的 drop
函数没有正确实现,可能会导致使用后释放漏洞。更具体地说,当分配区本身被丢弃时,分配在分配区中的值的引用可能会悬空。以下示例证明了这一点
struct GraphNode<'ctx> {
data: i32,
other_nodes: Vec<&'ctx GraphNode<'ctx>>,
}
impl<'ctx> Drop for GraphNode<'ctx> {
fn drop(&mut self) {
let mut s = 0;
for node in &self.other_nodes {
// The reference `node` which points to other nodes allocated in the same arena may
// dangle here.
s += node.data;
}
println!("{}", s);
}
}
为了解决这个问题,这个 crate 提供了一个安全的包装器 ArenaMut
,用于分配值的可变引用。每次安全包装器被 Deref
-ed,它都会检查引用的值是否已被丢弃。如果不幸地,引用的值已被丢弃,则程序会引发恐慌,从而防止发生未定义的行为。
用法
Arena
结构体表示一个分配区。