【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 标注时要谨慎,以免后续扩展带来不必要的麻烦。

相关文章
|
18天前
|
存储 Rust
【Rust】——所有权规则、内存分配
【Rust】——所有权规则、内存分配
31 0
|
18天前
|
存储 Rust 安全
Rust 笔记:Rust 语言中的 所有权 与 生命周期
Rust 笔记:Rust 语言中的 所有权 与 生命周期
140 0
|
18天前
|
Rust 算法 安全
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
【Rust中的所有权系统深入解析】A Deep Dive into Rust‘s Ownership System
38 0
|
18天前
|
Rust 编译器
【Rust】——函数(所有权)以及借用或引用
【Rust】——函数(所有权)以及借用或引用
24 0
|
18天前
|
存储 缓存 Rust
【Rust】——所有权:Stack(栈内存)vs Heap(堆内存)(重点)
【Rust】——所有权:Stack(栈内存)vs Heap(堆内存)(重点)
28 0
|
18天前
|
Rust 安全 编译器
深入Rust的所有权系统:理解变量的所有权
本文详细探讨了Rust编程语言中所有权系统的核心概念,包括变量的所有权、生命周期、借用规则和内存安全。通过理解这些概念,我们能够编写出更加高效、安全和可维护的Rust代码。
|
18天前
|
存储 Rust 安全
Rust核心功能之一(所有权)
Rust核心功能之一(所有权)
|
7月前
|
Rust 安全 编译器
Rust 基础入门 —— 2.3.所有权和借用 (二)
同一时刻,你只能拥有要么一个可变引用, 要么任意多个不可变引用 引用必须总是有效的 贴一个体验不错的学习链接恰饭:学习链接
47 0
|
7月前
|
Rust 安全 算法
Rust 基础入门 ——所有权 引言 :垃圾自动回收机制的缺陷。
能有这些问题的部分发生场景: 游戏开发:在游戏开发中,需要保持稳定的帧率和低延迟,以提供流畅的游戏体验。如果GC频繁触发或停顿时间过长,会导致游戏卡顿或掉帧,影响游戏的流畅度和响应性能。
56 0
|
7月前
|
存储 Rust 安全
Rust 基础入门 —— 2.3.所有权和借用
写在前面的序言 因为我们这里实际讲述的内容是关于 内存安全的,所以我们最好先复习一下内存的知识。
28 0