在 rust 中,有 Copy 和 Clone 两个 trait 用于“复制”,本文区别两者。
Clone traitstd::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 traitstd::marker::Copy triat 是一个 marker trait,定义如下:
pub trait Copy: Clone { }Copy trait 用于标记对象的“复制”只要浅拷贝即可,是可以简单、直接地逐位复制实现数据的完整拷贝。此时,拷贝将由编译器隐式实现。
拷贝隐式实现的含义
在 rust 中,变量赋值默认是移动语义(move semantics),比如:
#![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:?}"); // ❌
}当对象实现 Copytriat,变量赋值将是复制语义(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:?}"); // ✔️
}所以不是所有的类型都能实现 Copytrait 的 [1],比如:
String:栈上的指针,指向堆中的 buffer。Copy 只会拷贝栈上的指针,导致二次释放(double free)。Vec<T>:同样是指针,指向堆中的数组。&mut T:会导致创建多个可变引用总结起来:
的类型不能实现 Copy trait。
Copy 与 Droptrait 的关系
类型实现了 Droptriat,表明清理该类型对象比较复杂,而不是简单从栈上删除,该变量也就不应该实现 Copytriat。
rustc 事实上会阻止实现了 Drop trait 的类型实现 Copy trait。[2]
Clone trait 是 Copytriat 的 super-trait。想实现 Copytrait 一定要实现 Clonetrait,因为拷贝能力实际是由 Clonetrait 实现的,Copytrait 只是一个 marker triat,标记该类型的拷贝是低成本的。