Rust标准库的文档指出Cell
只能用于Copy
类型,在所有其他情况下都应该使用RefCell
,但没有确切解释原因。
在研究了Cell
和RefCell
的文档和代码之后,唯一重要的一点似乎是Cell
的get
函数。如果该值是Copy
类型,则可以只返回这样的副本。但是为什么克隆还不够好呢?
可以在RefCell
:之上直接实现set
功能
fn set<T>(r: &RefCell<T>, v: T) {
*r.borrow_mut() = v
}
只有当没有其他人持有对该值的引用时,这才有效。但如果可以克隆值,就可以这样做:
fn get<T: Clone>(r: &RefCell<T>) -> T {
r.borrow().clone()
}
让Cell
这样的类型与Clone
类型一起使用可以避免运行时借用检查的开销。我是不是遗漏了什么?
它不健全。DK.的评论是正确的,但你甚至不需要恐慌来造成破坏。一个有问题的场景是:
- 单元格(与
Option
一起(允许创建循环,即自引用类型 Clone
实现获得&self
引用- 因此,在存在循环的情况下,
Clone
实现可以访问正在克隆的细胞 - 因此,被克隆的对象可以覆盖它自己,而它对自己有一个普通的借用(即
&self
( - 借贷时过度书写是不健全的,因为它允许任意类型的双关语和其他不良行为。例如,假设有一个最初为
Ok(T)
的Result<T, E>
字段,引用内部的T
并用Err(R)
覆盖Result
。则CCD_ 23突然参考CCD_
这个例子归功于Huon Wilson,请参阅user.rust-lang.org线程"为什么Cell需要Copy而不是Clone?"?。他的文章深入探讨了限制的更多结构性原因,还包括一个完整的代码示例。
这是我的观点,但我不能直接将其与存在这种限制的真正原因联系起来。
我认为复制是"廉价的"(例如复制少数位(,克隆是"昂贵的"(如调用函数或更改数据(。如果这样的单元格使用Clone
,它将强制在每次使用时复制基础值(cell.get()
(。例如,使用CloneCell<Vec<T>>
意味着每个cell.get()
都需要调用内存分配器。这不是个好主意。
因此,限制使用Copy
类型可能是引导人们避免朝自己的脚开枪的一种方式。
公认的答案仍然是完全正确的(而且很有趣(,但我想提到Cell
在Rust 1.17中获得的一些额外工具,它们不需要内容是Copy
:
- CCD_ 32交换两个小区的内容
Cell::replace
在单元格中放入一个新值并返回旧值Cell::take
与replace
类似,使用Default::default()
值
注意这里与mem::swap
、mem::replace
和mem::take
的紧密平行。(尽管实际上最后一个直到Rust 1.40才稳定下来。(Cell
方法有效地做了同样的事情,但它们通过共享引用工作,而不需要可变引用。
对于实现Default
的类型,我们可以使用Cell::take
来完成与.clone()
非常相似的事情,只需再执行几个步骤:
fn clone_from_cell<T>(cell: &Cell<T>) -> T
where
T: Clone + Default,
{
let val: T = cell.take();
let clone: T = val.clone();
cell.set(val);
clone
}
对于实现Clone
但不实现Default
的类型(有些不常见,但例如NonZeroU32
(,请注意,Option<T>
实现Default
而与T
无关,因此Cell<Option<T>>
可以是"0";克隆的";对任何CCD_ 51使用该函数。