为什么 Rust 中的 Cell 只能用于 Copy 而不是 Clone 类型? [英] Why can Cell in Rust only be used for Copy and not Clone types?
问题描述
Rust 标准库的文档声明 Cell
只能用于 Copy
类型,在所有其他情况下应该使用 RefCell
,但没有解释确切原因.
The documentation of the Rust standard library states that Cell
can be only used for Copy
types and that in all other cases one should use RefCell
, but does not explain exactly why.
在研究了 Cell
和 RefCell
的文档和代码后,唯一看起来很重要的地方是 get
函数Cell
.如果值是 Copy
类型,则可以返回这样的副本.但为什么克隆不够好?
After studying the documentation and the code of both Cell
and RefCell
, the only point where it seems to be important is the get
function of Cell
. If the value is a Copy
type then one can just return such a copy. But why is a clone not good enough?
可以直接在RefCell
之上实现set
函数:
One could directly implement the set
function on top of RefCell
:
fn set<T>(r: &RefCell<T>, v: T) {
*r.borrow_mut() = v
}
这仅在没有其他人持有对该值的引用时才有效.但是如果可以克隆值,就可以这样做:
This only works as long as no one else is holding a reference to the value. But if the value can be cloned, one can just do that:
fn get<T: Clone>(r: &RefCell<T>) -> T {
r.borrow().clone()
}
让像 Cell
这样的类型与 Clone
类型一起工作将避免运行时借用检查的开销.我在这里遗漏了什么吗?
Having a type like Cell
working with Clone
types would avoid the overhead of the run-time borrow checking. Am I missing anything here?
推荐答案
它不健全.DK的评论.是在正确的轨道上,但您甚至不需要恐慌就可以造成严重破坏.一个有问题的场景是这样的:
It's unsound. The comment by DK. is on the right track but you don't even need a panic to cause havoc. One problematic scenario is this:
- 单元格(与
Option
一起)允许创建循环,即自引用类型 Clone
实现获得一个&self
引用- 在存在循环的情况下,
Clone
实现可能因此访问被克隆的单元 - 因此被克隆的对象可以覆盖自身,而它对自身有一个普通的借用(即,
&self
) - 在借用时覆盖是不合理的,因为它允许任意类型的双关语和其他坏处.例如,假设有
Result
字段最初是Ok(T)
,引用里面的T
并覆盖Result
带有Err(R)
.然后&T
突然引用了一个E
值.
- Cells (together with
Option
) allow creating cycles, i.e., self-referential types - The
Clone
implementation gets a&self
reference - In the presence of a cycle, the
Clone
implementation may thus access the cell that's being cloned - Therefore the object being cloned can overwrite itself, while it has an ordinary borrow to itself (namely,
&self
) - Overwriting while borrowing is unsound because it allows arbitrary type punning and other badness. For example, suppose there's
Result<T, E>
field that's initiallyOk(T)
, take a reference to theT
inside and overwrite theResult
with anErr(R)
. Then the&T
suddenly refers to anE
value.
此示例归功于 Huon Wilson,请参阅 user.rust-lang.org 主题 为什么 Cell 需要复制而不是克隆?.他的文章深入探讨了限制的更多结构性原因,还包括一个完整的代码示例.
Credit for this example goes to Huon Wilson, see the user.rust-lang.org thread Why does Cell require Copy instead of Clone?. His write-up goes into more structural reasons for the restrictions and includes a complete code example, too.
这篇关于为什么 Rust 中的 Cell 只能用于 Copy 而不是 Clone 类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!