【Rust 中级教程】 11 所有权与trait(4)

简介: 【Rust 中级教程】 11 所有权与trait(4)

0x00 开篇


上一篇文章介绍了所有权的概念,本篇文章将再次向大家介绍两个常见的 trait —— Copy 和 Clone。在所有权的介绍过程中,我总是说在堆上保存数据的类型才会拥有所有权,其实并不是很准确。本篇文章将厘清哪些数据拥有所有权,哪些数据不需要所有权。本篇文章的阅读时间大约 8 分钟。


0x01 Clone


clone 方法是创建一个独立的副本并返回,方法的返回值类型是 Self。以 String 为例,当我们使用 clone 方法时,不仅需要复制栈上的指针,还要复制指针指向堆上的字符串。没错,可以将 clone 方法理解为是深复制,因此,调用 clone 方法是会消耗一定的时间和内存。

源码(clone.rs):

pub trait Clone: Sized {
    fn clone(&self) -> Self;
}

Clone 是可以与 derive 属性一起使用的。我们可以为自定义的结构体来实现 Clone trait。

示例代码如下:

#[derive(Debug, Clone)]
struct Student {
    name: String,
}
fn main() {
    let student1 = Student { name: String::from("xiaoming") };
    let student2 = student1.clone();
    println!("student1 = {:?}", student1);
    println!("student2 = {:?}", student2);
    // 运行结果
    // student1 = Student { name: "xiaoming" }
  // student2 = Student { name: "xiaoming" }
}

如何确定是真的发生深复制了呢?没错,让我们来看下内存。


0a2653c851af460fa595bd959398a8f1.png


image-20221031214143607


下断点,看内存。从上图可以看出,student1 和 student2 的地址不一样,并且 student1 和 student2 中字符串的地址也是不一样的。student1 字符串的地址是 0x000002433bbbbb30 ,student1 字符串的地址是 0x000002433bbbbb50。定位到该地址(下图)查看。


2d65d23f6d4748949b924e4057485923.png


所以, Clone trait 是用于标记可以对值深复制的类型,堆上的数据和栈上的数据。其实 String 类型默认也实现了 Clone trait,所以这里需要注意,如果结构体或者枚举类型被 #[derive(Clone)]标记,那么结构体或者枚举的每个字段也必须实现 Clone trait。

另外,在介绍所有权时,如果我们把一个变量传递到一个函数体内,那么在原作用域内这个 变量将会变为无效。其实,我们可以通过 clone 方法,来实现深复制,将深复制后的数据传递到函数体内,而原有的变量则不会失去所有权。这样的做法并不推荐,会损耗性能。

示例代码:

fn main() {
    // 创建 s1
    let s1 = String::from("Zhang San");
    // 深复制一个 s1 传递给 print_name 函数
    print_name(s1.clone());
    // 打印原来的 s1
    println!("{}", s1);
    // 运行结果
    // My name is Zhang San
  // Zhang San
}
fn print_name(name: String) {
    println!("My name is {}", name);
}


0x02 Copy


在 Rust 中,对于大部分类型来讲,在赋值时都是转移值,而不是复制值。Copy trait 继承自 Clone trait。

源码(marker.rs):

pub trait Copy: Clone {
    // Empty.
}

从源码可以看到 Copy trait 是空的,其实在 Rust 中,Copy trait 是一个标记类型。**通常被标记为 Copy trait 的这些类型在变量赋值时不会发生转移。**被标记为 Copy trait 的类型通常数据仅保存在栈上,对于这些类型进行复制(copy)操作时内存耗时少,代价也小。


Rust 标准库中,默认基本数据类型都标识了 Copy 类型,如下:

  • 整数类型:usize u16 u32 u64 u128
    isize i16 i32 i64 i128
  • 浮点类型:f32 f64
  • 布尔类型:bool
  • 字符类型:char

以及其它一些 Sized 类型的共享引用(不可变引用)和原始指针等。——暂时可忽略

Copy 是可以与 derive 属性一起使用的。我们可以为自定义的结构体来实现 Copy trait。

示例代码:

fn main() {
        let rec1 = Rectangle { width: 10, height: 5 };
    // 注意:同样需要使用 clone 方法
    let rec2 = rec1.clone();
    println!("rec1 的宽 : {}, 高 : {}", rec1.width, rec1.height);
    println!("rec2 的宽 : {}, 高 : {}", rec2.width, rec2.height);
    // 运行结果
    // rec1 的宽 : 10, 高 : 5
    // rec2 的宽 : 10, 高 : 5
}


0x03 扩展——struct 和 enum 属于什么类型?


假设,我们将含有字符串的 struct 强制标记 Copy trait,会发生什么?


6de278e6d6694ce5bb08e7e842b7e74b.png


编译会发生错误,如果一个 strut 或者 enum 是实现了Copy,那么它所属的所有字段都必须实现 Copy。因此 Copy 类型包含的类型较少 ,限制性非常大。至于 strut 或者 enum 属于什么类型,这取决于它包含的类型了。


0x04 小结


本篇文章通过所有权了解了另外两个常见的 trait ——CopyClone。Rust 基本库为基本数据类型实现了 Copy。通俗来讲,Clone主要标记可以对值进行深复制的类型,而 Copy主要标记可以进行浅复制的类型。还有要注意的一点,所有我们自定义的类型都默认属于非Copy类型。如果自定义的结构体或者枚举的所有字段都是Copy类型,那我们需要使用 #[derive(Copy,Clone)]标注。在各种开发场景中使用 Copy 标注时要谨慎,以免后续扩展带来不必要的麻烦。

相关文章
|
7月前
|
存储 Rust
【Rust】——所有权规则、内存分配
【Rust】——所有权规则、内存分配
|
1月前
|
Rust 安全
深入理解Rust语言的所有权系统
深入理解Rust语言的所有权系统
36 0
|
3月前
|
存储 Rust
30天拿下Rust之Trait
30天拿下Rust之Trait
60 0
|
4月前
|
存储 Rust 安全
30天拿下Rust之所有权
在编程语言的世界中,Rust凭借其独特的所有权机制脱颖而出,为开发者提供了一种新颖而强大的工具来防止内存错误。这一特性不仅确保了代码的安全性,还极大地提升了程序的性能。在Rust中,所有权是一种编译时检查机制,用于追踪哪些内存或资源何时可以被释放。每当一个变量被赋予一个值(比如:字符串、数组或文件句柄)时,Rust会确定这个变量是否“拥有”这个值,拥有资源的变量负责在适当的时候释放这些资源。
43 5
|
5月前
|
存储 Rust 安全
【Rust学习】04_所有权
所有权是 Rust 最独特的特性,对语言的其余部分有着深远的影响。它使 Rust 能够在不需要垃圾收集器的情况下保证内存安全,因此了解所有权的运作方式非常重要。在本章中,我们将讨论所有权以及几个相关功能:借用、切片以及 Rust 如何在内存中布局数据。
33 1
|
7月前
|
存储 Rust 安全
Rust 笔记:Rust 语言中的 所有权 与 生命周期
Rust 笔记:Rust 语言中的 所有权 与 生命周期
200 0
|
5月前
|
Rust
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
rust 引用了Trait的实现,为什么还需要引入Trait 才能调用实现的方法
|
6月前
|
Rust 安全 开发者
Rust引用、借用和所有权详解
Rust引用、借用和所有权详解
|
7月前
|
Rust 算法 安全
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
107 0
|
7月前
|
Rust 安全 编译器
深入Rust的所有权系统:理解变量的所有权
本文详细探讨了Rust编程语言中所有权系统的核心概念,包括变量的所有权、生命周期、借用规则和内存安全。通过理解这些概念,我们能够编写出更加高效、安全和可维护的Rust代码。