• Rust中Copy和Clone之间有什么区别?
  • 发布于 2个月前
  • 186 热度
    0 评论
Copy特征和Clone特征是Rust编程语言中的重要概念。从理论上讲,它们很简单,但在现实中很难应用,特别是对于来自Python、Java等语言的新程序员。在这篇文章中,我们将了解在Rust中Copy和Clone意味着什么。

Rust的所有权
在讨论rust中的Copy和Clone特征之前,让我们先简要了解一下所有权。在每一种编程语言中,都有在执行时管理计算机内存的方法。支持垃圾收集器的语言在程序执行时定期查找不再使用的内存,而在C/C++等其他语言中,程序员必须显式地分配和释放内存。在Rust中,内存通过所有权来管理。所有权是编译器检查、管理内存的一组规则。如果违反了任何所有权规则,程序甚至不会编译。

所有权规则
因为所有权对许多程序员来说是一个新概念,需要一些时间来适应,但是记住这些规则将有助于编写安全和高效的代码。
1,在Rust中,每个值都有一个所有者,即赋值给它的变量。
2,一次只能有一个所有者。虽然,多个变量可以保存对相同值的引用或借用相同值,但该值只有一个所有者。
3,当所有者超出范围时,该值将被删除。

Rust中的内存分配(堆栈vs堆)
Rust在运行时同时提供堆栈和堆内存部分。在Rust中,理解内存分配是很重要的,因为值如何存储在内存中会影响语言的行为,并帮助程序员做出某些决定。在堆和堆栈中存储值的方式是不同的。就像堆栈数据结构一样,堆栈按顺序存储值,它以相反的顺序获取并删除值,即后进先出的顺序。在堆中,当我们想要存储数据时,我们请求一定的空间。内存分配器在堆中找到一个足够大的空间来存储该值,将其标记为正在使用,并返回指向该位置地址的指针。

压入堆栈比在堆上分配快,因为分配器不必寻找存储新数据的地方。像u16、u32、i32和f64这样的值都存储在堆栈中,因为它们的大小在编译时是已知的。
动态大小类型(如String和Vec)存储在堆中,因为它们的大小可以增加或缩小,并且没有固定的大小。

Rust中的Clone特征
在Rust中,一些简单的类型是“隐式可复制的”,当你分配它们或将它们作为参数传递时,接收者将获得一个副本,原始值保留在适当的位置。对于其他类型,必须通过实现Clone特征并调用Clone()方法显式地进行复制。

Clone特征定义了显式创建对象T的深度拷贝的能力。当我们为类型T调用Clone时,它会执行创建新T所需的所有任意复杂操作。
fn main() {
    // 堆代码 duidaima.com
    let name = String::fron("IntMain!");
    // value的所有权由name转移到new_name 
    let new_name = name;
    // 错误:name被移动!
    println!("Old name = {} and new name ={}", name, new_name);
}
在上面的例子中,根据所有权规则,name分配给new_name,将String实例的所有权转移到new_name,由于只能有一个所有者,因此name被删除。因此,为了获得一个深度拷贝,我们需要显式地调用clone()方法。
fn main() { 
    let name = String::fron("IntMain!"); 
    // 将name的深度拷贝分配给new_name
    // 对象的显式复制 
    let new_name = name.clone(); 

    // 编译良好,没有错误
    println!("Old name = {} and New name ={}", name, new_name); }
}
由于深度拷贝,name和new_name都可以独立地删除它们的堆缓冲区。
注意:Clone特征并不总是创建深度副本。类型可以自由地以任何它们想要的方式实现Clone。例如,Rc和Arc只增加一个引用计数,而不是创建一个深度副本。

Rust中的Copy特征
rust中的Copy特征定义了隐式复制对象的能力。Copy行为不可重载,它总是一个简单的位拷贝。这适用于具有固定大小且完全存储在堆栈上的类型。
fn main() { 
    let num = 987; 
    // new_num是num的副本 
    let new_num = 987; 

    //编译良好,没有错误
    println!("Old num = {} and New num ={}", nun, new_num); }
}
上面的例子并不与所有权规则相矛盾,因为i32是Copy类型,因为它们存储在堆栈中,很容易复制它们,因此num的值被隐式复制到new_num。正如我们可以想象的那样,不是每一种类型都可以实现Copy,最好的例子是String,它实现了Clone而不是Copy。

Copy类型仅应用于存储在堆栈中的类型,如整数、浮点数、及其所有字段都是Copy类型的Struct。
// 这个结构实现了Copy特性
// 它的所有字段都是Copy类型
#[derive(Clone, Copy)]
struct copy_and_cloneable{
    num1: i32,
    num2: i32,
    num3: bool,
}

// 这个结构没有实现Copy trait
// String不是Copy类型
#[derive(Clone, Copy)]
stuct cloneable_only{
    num1: i32,
    name: String,     //Not a Copy type
}
注意:Clone是Copy的super trait,所以所有实现Copy的都必须实现Clone,因此当一个类型实现Copy时,Clone实现只是返回*self。

总结
在rust中,一旦你理解了rust的内存分配,就很容易理解Copy和Clone之间的区别。可推入堆栈的类型实现了Copy特征,可推入堆的类型实现了Clone特征。由于深度复制是昂贵的,因此我们需要显式调用clone()方法,否则默认情况下发生移动。
用户评论