在 rust 中,有 Copy
和 Clone
两个 trait 用于“复制”,本文区别两者。
Clone
trait
std::clone::Clone
triat 的定义如下:
pub trait Clone: Sized {
// Required method
fn clone(&self) -> Self;
// Provided method
fn clone_from(&mut self, source: &Self) { ... }
}
Clone
triat 用于给对象加上“复制”的能力,需要 x.clone()
来显式调用。对象的“复制”既可以是浅拷贝,也可以是深拷贝。
Copy
trait
std::marker::Copy
triat 是一个 marker trait,定义如下:
pub trait Copy: Clone { }
Copy
trait 用于标记对象的“复制”只要浅拷贝即可,是可以简单、直接地逐位复制实现数据的完整拷贝。此时,拷贝将由编译器隐式实现。
拷贝隐式实现的含义
在 rust 中,变量赋值默认是移动语义(move semantics),比如:
代码语言:javascript复制#![allow(unused)]
fn main() {
let s = String::from("Hello, world!"); // String implements Clone trait but not Copy trait
let scopy = s; // s is move to scopy, and so cannot be used
println!("{s:?}"); // ❌
}
当对象实现 Copy
triat,变量赋值将是复制语义(copy semantics):
#![allow(unused)]
fn main() {
let s: i32 = 12; // i32 implements Copy trait
let scopy = s; // s is copy to scopy, which still can be used
println!("{s:?}"); // ✔️
}
所以不是所有的类型都能实现 Copy
trait 的 [1],比如:
String
:栈上的指针,指向堆中的 buffer。Copy
只会拷贝栈上的指针,导致二次释放(double free)。Vec<T>
:同样是指针,指向堆中的数组。&mut T
:会导致创建多个可变引用
总结起来:
- 不是完全在栈上
- 浅拷贝会破坏 rust 的所有权规则
的类型不能实现 Copy
trait。
Copy
与 Drop
trait 的关系
类型实现了 Drop
triat,表明清理该类型对象比较复杂,而不是简单从栈上删除,该变量也就不应该实现 Copy
triat。
rustc 事实上会阻止实现了 Drop
trait 的类型实现 Copy
trait。[2]
总结
Clone
trait 是 Copy
triat 的 super-trait。想实现 Copy
trait 一定要实现 Clone
trait,因为拷贝能力实际是由 Clone
trait 实现的,Copy
trait 只是一个 marker triat,标记该类型的拷贝是低成本的。
- https://oswalt.dev/2023/12/copy-and-clone-in-rust/ ↩︎
- https://doc.rust-lang.org/std/marker/trait.Copy.html#when-cant-my-type-be-copy ↩︎