Clone过借用检查
说明
借用检查阻止了Rust用户开发不安全的代码,以此保证:只存在一个可变引用,或者(许多)不可变引用。如果编写的代码不符合这些条件,而开发者通过克隆变量来解决编译器错误,就会产生这种反模式。
示例
#![allow(unused)] fn main() { // 定义任意变量 let mut x = 5; // 借用 `x`(先clone) let y = &mut (x.clone()); // 由于 x.clone(), x 并未被借用, 这行代码可以运行。 println!("{}", x); // 用这个借用做点什么,防止因Rust优化直接砍掉这个借用 *y += 1; }
出发点
用这种模式来解决借用检查令人困惑的问题是很诱人的,特别是对于初学者来说。然而,这有严重的后果。使用.clone()
会导致数据被复制。两者之间的任何变化都不会同步——因为会有两个完全独立的变量存在。
有种特殊情况—— Rc<T>
被设计为智能处理 clone
。它在内部确切管理着一份数据的副本,clone它只会clone引用。
还有Arc<T>
,它提供堆分配类型T的共享所有权。对Arc
调用.clone()
会得到新的Arc
实例,它指向和源Arc
相同的栈分配,增加引用计数。
一般来说,应该经过深思熟虑,充分了解其后果再clone。如果用clone消除借用检查器报错,很可能你使用了这种反模式。
即使.clone()
是坏模式的预兆,有时编写低效率的代码是可以的,比如这些情况时:
- 开发者不大懂所有权
- 代码没有什么速度或内存限制(如黑客马拉松项目或原型)。
- 借用检查器太复杂了,而你更愿意优化可读性,而非性能
如果你怀疑做了不必要的clone,在评估是否需要clone之前,先去弄懂《Rust Book》的所有权章节。
此外要保证一直给你的项目跑cargo clippy
,它可以判断一些.clone()
调用不必要的情况,比如甲,乙,丙或者丁.