Cell只是一个内嵌存储/ Package 类型,它控制内部类型T的可变性/访问语义,并提供内部可变性功能。根据rust文档,Deref没有为Cell<T>类型实现。为什么会这样呢?Cell<T>的示例始终处于有效状态(内存始终有效且已初始化)。这意味着当在Cell<T>上实现Deref时,它不可能死机,这是实现Deref的要求之一。为Cell<T>提供一个Deref impl会很方便。
Deref
Cell<T>
55ooxyrt1#
但这是不合理的!Cell的前提是它 * 从不 * 给出对其内容的引用(实际上,这正是它的兄弟*Ref*Cell所做的),因此对它的所有读写操作都是原子的。作为其实现的一部分,Cell有一个set方法,其签名如下:
Cell
*Ref*Cell
set
fn set(&self, val: T)
这会改变&Cell<T>的值,即共享引用后面的单元格的值2.这就是内在可变性的工作原理。现在想象一下,你可以创建一个对Cell内容的引用:
&Cell<T>
let cell = Cell::new(1); let one: &i32 = &*cell;
......还有人调用了set方法:
cell.set(2);
现在我们已经变异了一个共享引用(&T)指向的数据,它是UB 3!
&T
println!("one = {}", *one); // one = 2 !!!
Playground link1:不是“线程安全”意义上的2:它可以通过使用UnsafeCell来很好地做到这一点:一个编译器内置说:“相信我编译器,我可以强制执行借用规则,所以你不必”3:UnsafeCell的 * 外部 *(&T不知道Cell)
UnsafeCell
1条答案
按热度按时间55ooxyrt1#
但这是不合理的!
Cell
的前提是它 * 从不 * 给出对其内容的引用(实际上,这正是它的兄弟*Ref*Cell
所做的),因此对它的所有读写操作都是原子的。作为其实现的一部分,
Cell
有一个set
方法,其签名如下:这会改变
&Cell<T>
的值,即共享引用后面的单元格的值2.这就是内在可变性的工作原理。现在想象一下,你可以创建一个对
Cell
内容的引用:......还有人调用了
set
方法:现在我们已经变异了一个共享引用(
&T
)指向的数据,它是UB 3!Playground link
1:不是“线程安全”意义上的
2:它可以通过使用
UnsafeCell
来很好地做到这一点:一个编译器内置说:“相信我编译器,我可以强制执行借用规则,所以你不必”3:
UnsafeCell
的 * 外部 *(&T
不知道Cell
)