1.Copy Trait定义
Copy Trait声明如下
pub trait Copy: Clone { }
这个Trait中没有方法,这意味着它是一个标记Trait,它用于表明对类型的某种约束,而这种约束在类型系统中并没有直接体现。就 Copy Trait而言,这个标记的含义是,对保存某个项的内存进行逐位复制就能得到一个正确的新项。实际上,这个Trait是一个标记,表明某个类型是 “普通旧数据”(POD)类型。我也是第一次知道 POD 这个词。它大致是指内存布局连续,可以通过 memcopy 进行内存复制的数据类型。
2.Copy与Clone的关系
- 实现要求:实现 Copy 特质的类型必须同时实现 Clone 特质。这是因为复制本质上也是一种克隆操作,但两者的实现细节有所不同。
struct Point {
x: i32,
y: i32,
}
// 编译报错,需要实现Clone Trait
impl Copy for Point{
}
上面的代码会编译报错,需要调整为下面的代码。
struct Point {
x: i32,
y: i32,
}
impl Clone for Point{
fn clone(&self) -> Self {
Self { x:2,y:12 }
}
}
// 可以正常运行
impl Copy for Point{
}
- 调用差异:当复制一个实现了 Copy 特质的类型实例时,并不会调用 clone() 方法。编译器会直接进行位复制,不会执行用户为 clone() 方法定义的代码。例如:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Clone for Point{
fn clone(&self) -> Self {
print!("代码被执行");
Self { x:2,y:12 }
}
}
impl Copy for Point{}
#[test]
fn test_copy(){
let src_obj = Point{x:1,y:1};
let target_obj = src_obj;
print!("value is {:?}", src_obj);
}
这也意味着 Clone 特质约束可能会有点令人困惑:尽管实现 Copy 的类型必须实现 Clone,但当该类型的一个实例被复制时,clone() 方法并不会被调用,上面的第9行不会打印 —— 编译器会在不涉及任何用户定义代码的情况下创建新的对象。
与用户定义的标记Trait不同,Copy Trait对编译器有着特殊的意义(std::marker 中的其他几个标记Trait也是如此),它不仅仅可用于约束 —— 它会使编译器从move语义转变为copy语义。
3.Copy与Drop
Copy的定义类似为:trait Copy: Clone + !Drop {}。在实现Copy Trait时,编译器追加了一个反向的!Drop。
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Clone for Point{
fn clone(&self) -> Self {
print!("代码被执行");
Self { x:2,y:12 }
}
}
// 编译报错
impl Copy for Point{}
impl Drop for Point{
fn drop(&mut self) {
todo!()
}
}
在 Rust 里,当一个类型实现了 Drop Trait(也就是有析构函数,因为实现 Drop Trait意味着定义了类型在离开作用域时的清理逻辑),就不能再实现 Copy Trait了。这是因为 Copy Trait代表着类型可以通过简单的位复制来创建副本,而实现 Drop Trait的类型往往涉及资源管理(如释放内存、关闭文件句柄等),如果允许这样的类型实现 Copy Trait,在复制和销毁副本时就可能会导致资源的重复释放等问题,破坏内存安全。
例如下面的代码是不合法的:
4.复合类型的Copy
如果一个类型的所有组成部分都是 Copy 的,那么在很多情况下,最好自动派生 Copy。编译器中有一个名为
missing_copy_implementations 的 lint 项目,用于指出这种情况,但它默认是关闭的。我第一次知道
missing_copy_implementations。由于没有关于它默认关闭的解释,我查阅文档后发现如下内容:
“在历史上(1.0 版本之前),复合类型的组成部分都是Copy的,复合类型会自动被标记为 Copy。后来改变了这一规则,需要通过实现 Copy Trait来显式制定。这个改变,主要因为 Copy 类型可能会导致大数据的意外复制,从而影响性能。
#[derive(Debug)]
struct Point {
x: i32, // Copy类型
y: i32, // Copy类型
}
#[test]
fn test_copy(){
let src_obj = Point{x:1,y:1};
let target_obj = src_obj;
print!("value is {:?}", src_obj); // 执行报错,value borrowed here after move
}
以上就是本次对Copy Trait的介绍,希望对你有帮助。