我需要 fork 流行的 gc crate 来为 Trace + Finalize
实现 Cell
。这就是库为 Vec
实现这些的方式:
impl<T: Trace> Finalize for Vec<T> {}
unsafe impl<T: Trace> Trace for Vec<T> {
custom_trace!(this, {
for e in this {
mark(e);
}
});
}
我正在尝试对 Cell<T>
执行相同的操作,但它仅适用于 T: Copy
。 as_ref()
中没有 Cell
方法,那么我怎样才能做得更好?
impl<T: Trace> Finalize for Cell<T> {}
unsafe impl<T: Trace + Copy> Trace for Cell<T> {
custom_trace!(this, {
// this: &Cell<T>
mark(&this.get());
});
}
答案 0 :(得分:2)
无法从 &T
获取 Cell<T>
。事实上,这就是 Cell
的全部意义所在。请允许我解释一下:
Rust 的核心思想之一是,同时发生的别名和变异是各种罪恶的根源。因此 (multiple references (aliasing) & mutation)
不能在 Rust 中发生。有两种特殊情况我们可以排除这种情况:
&mut T
存在的原因。我们称它为“可变引用”,但这里的重点是编译器保证它是唯一的引用,防止出现别名;在这种情况下,我们可以自由地改变 T
而不用担心。如果我们有活的 &T
,情况就会相反:使用 &T
可以使用别名,并且禁止突变。Cell<T>
就是这种情况,它不允许从其内部(它的一个单元格)获取 &T
。由于没有引用,我们可以随时随意修改值,因为不可能有任何优秀的观察者。这就是为什么您可以使用对 Cell<T>
的不可变引用来修改 T
(例如 mycell.set(123)
)。这两种特殊情况允许 Rust 在编译时防止别名和变异,而不会给程序员带来太多麻烦。
据您的代码片段判断,这正是 Cell
必须防止的那种情况:mark()
函数采用 &T
并且可能对它,包括存储它、传递它、复制它或获取对 T
成员的引用。 Cell
可以防止引用逃逸到其他作用域中,从而允许在不“害怕”别名的情况下进行突变。
您可能首先要重新考虑为什么会有 Cell
。这可能表明T
的值根本不能转义,因为其他地方的代码会使 T
发生变异,然后您将使用停滞/过时的值。
您也许可以使用 RefCell
,它是 Cell
的大哥,它使用管理所有访问的代理类型在运行时跟踪别名/变异。您可以从 &T
中获取 RefCell
,但是您的实现必须保证它永远不会尝试在别名时发生变异(编译器无法捕获,但是运行时会导致恐慌)。