为什么Rust中的Cell只能用于复制而不能用于克隆类型?

时间:2016-09-23 19:03:58

标签: rust traits

Rust标准库的文档指出Cell只能用于Copy类型,而在所有其他情况下,应该使用RefCell,但不能解释原因。

在研究了CellRefCell的文档和代码之后,唯一重要的一点是get的{​​{1}}函数。如果值是Cell类型,则可以返回此类副本。但为什么克隆不够好呢?

可以在Copy

之上直接实现set功能
RefCell

只有在没有其他人持有对该值的引用时,这才有效。但是,如果可以克隆该值,可以这样做:

fn set<T>(r: &RefCell<T>, v: T) {
    *r.borrow_mut() = v
}

使用类似fn get<T: Clone>(r: &RefCell<T>) -> T { r.borrow().clone() } 的类型处理Cell类型可以避免运行时借用检查的开销。我在这里错过了什么吗?

3 个答案:

答案 0 :(得分:10)

它不健全。 DK的评论。是在正确的轨道上,但你甚至不需要恐慌造成破坏。一个有问题的情况是:

  1. 单元格(与Option一起)允许创建循环,即自引用类型
  2. Clone实施获得&self参考
  3. 在存在循环的情况下,Clone实现可以访问正在克隆的单元格
  4. 因此克隆的对象可以覆盖自己,而它本身就有一个普通的借用(即&self
  5. 在借用时被覆盖是不合理的,因为它允许任意类型的惩罚和其他不良。例如,假设Result<T, E>字段最初为Ok(T),请引用内部的T并使用Result覆盖Err(R)。然后&T突然引用E值。
  6. 此示例的信用转到Huon Wilson,请参阅user.rust-lang.org帖子Why does Cell require Copy instead of Clone?。他的文章涉及更多结构性原因,包括一个完整的代码示例。

答案 1 :(得分:2)

这是我的意见,但我不能直接将其与存在这种限制的真正原因联系起来。

我认为副本为&#34;廉价&#34; (例如复制一些比特)和克隆作为&#34;昂贵的&#34; (例如,进行函数调用或更改数据)。如果这样的单元格使用Clone,它将强制在每次使用时复制基础值(cell.get())。例如,使用CloneCell<Vec<T>>意味着每个cell.get() 都需要调用内存分配器。这不是一个好主意。

因此限制Copy类型可能是引导人们远离脚部射击的一种方式。

答案 2 :(得分:0)

接受的答案仍然完全正确(并且引人入胜),但我想提一下 Cell 在 Rust 1.17 中获得的一些额外工具,它们不需要内容为 Copy

  • Cell::swap 交换两个单元格的内容。
  • Cell::replace 将新值放入单元格并返回旧值。
  • Cell::take 类似于 replace,使用 Default::default() 值。

注意这里与 mem::swapmem::replacemem::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 而不管 { {1}},所以 T 可以通过这种方式“克隆”到任何 Cell<Option<T>>

T