30、Copy trait

复制值(第二部分)

与上一节的例子一样,但是有点不同,使用u32而不是String作为类型。

fn consumer(s: u32) { /* */ }

fn example() {
     let s: u32 = 5;
     consumer(s);
     let t = s + 1;
}

它的编译不会发生错误,Stringu32之间是存在差别的,这个差别让u32不需要Clone就可以工作。

Copy

Copy是Rust标准库中定义的另外一种trait。

pub trait Copy: Clone { }

这是一个marker trait(标记trait),就像Sized一样。

如果一个类型实现了Copy,就不需要调用.clone()来创建该类型的新实例了,Rust会隐式地完成这个操作。

 

u32就是一个实现Copy地一个类型,这就是上面的编译没有错误地原因。当调用consumer(s)时,Rust通过执行s的bitwise copy按位复制创建了一个新的u32实例。

这一切都是隐式执行的,我们不需要做任何操作。

什么可以实现Copy

Copy不等同于自动Clone,尽管它看起来就是这么一回事。Copy的重点在于:类型必须满足一些条件才能实现Copy

 

首先,它必须实现Clone,因为CopyClone的一个subtrait(子trait)。这是有理有据的:如果Rust可以隐式的创建一个类型的新实例,那么它也能够通过调用.clone()来显式的创建一个新实例。

 

更确切的说,要想实现Copy,必须满足以下条件:

  • 该类型部管理任何超出了其在内存中占用的std::mem::size_of字节以外的其他资源(例如:堆内存、文件句柄等等)。
  • 该类型不是可变引用(&mut T)。

如果上面这两个条件都满足,那么Rust可以通过对原始实例执行按位复制来安全的创建该类型的新实例——这经常被称为memcpy操作,与执行按位复制的C标准库的函数同名。

 

示例一、String

String是一种不能实现Copy的类型。

这是因为String管理一个额外的资源:存储字符串数据的堆分配的内存缓冲区。

 

假设Rust允许为String实现Copy

那么通过对原始实例进行按位复制的操作创建新的String实例的时候,原始实例和新的实例都将指向相同的内存缓冲区。

String如果实现了Copy

这个可不行,当两个String超出范围时,它们都会尝试释放内存缓冲区,从而导致double-free错误。同样的,创建两个指向同一内存缓冲区的不同的&mut String引用,也会违反Rust的借用规则导致错误。

示例二、u32

不仅是u32,所有的整数类型都实现了Copy。

integer只是内存中表示数字的字节而已。如果我们复制这些字节,我们可以得到另一个完全有效的integer实例,不会发生什么问题,所以Rust允许这么做。

示例三、&mut u32

当我们引入所有权和可变借用时,有一条规则我们很清楚:在任何给定的时间里,一个值只能有一个可变借用。

这就是为什么&mut u32没有实现Copy,尽管u32实现了Copy。

 

如果&mut u32实现了Copy,我们就可以在相同时间为同一个值创建多个可变引用,并在多个地方修改它,这就违反了Rust的借用规则!所以,无论T是什么,&mut T永远不会实现Copy。

实现Copy

在多数情况下,我们不需要手动实现Copy,我们还是可以使用派生宏:

#[derive(Copy, Clone)]
struct MyStruct {
    field: u32,
}

练习

// TODO: implement the necessary traits to make the test compile and pass.
/* TODO */
pub struct WrappingU32 {
    value: u32,
}

impl WrappingU32 {
    pub fn new(value: u32) -> Self {
        Self { value }
    }
}

/* TODO */

这道题让我们实现Copy trait,不过我看test考察的东西不是单纯的Copy trait啊。代码就得那样写,我都不知道因为什么过不去的,后面再来认真看看(

// TODO: implement the necessary traits to make the test compile and pass.
#[derive(Clone,Copy,PartialEq,Debug)]
pub struct WrappingU32 {
    value: u32,
}

impl WrappingU32 {
    pub fn new(value: u32) -> Self {
        Self { value }
    }
}

impl std::ops::Add for WrappingU32 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self::new(self.value.wrapping_add(rhs.value))
    }
}

 

这一节的学习就先到这了…

阅读剩余
THE END