四时宝库

程序员的知识宝库

【rust基础】Copy Trait介绍

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的介绍,希望对你有帮助。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接