【Rust 中级教程】 09 所有权(2)

简介: 【Rust 中级教程】 09 所有权(2)

0x00 开篇


不知道大家对前一篇文章的概念搞懂了没有呢?本篇文章将通过与 Python、C++语言对比来解释移动语义。本篇文章的阅读时间大约 10 分钟。


0x01 变量绑定与所有权


我们在声明变量时,使用的是 let 关键字,这里的变量,本质上是一种绑定关系。let 将一个变量和值绑定在一起,变量则对这个值拥有所有权。对值拥有所有权可能说的有点儿模糊,实际是应该说 let 的作用是将变量与值的内存空间进行绑定,使得变量对值所在的内存空间拥有所有权。这就会出现对于每一块内容空间都拥有为一个绑定变量与其对应,非特殊场景下不允许多个变量同时指向同一块内存空间。


PS:特殊场景指的是 共享所有权,后面的章节会讲到,请先忽略这个概念。


0x02 转移(Move)


在 Rust 中,在一些需要在堆上保存数据的类型来说,对于变量之间赋值,向函数传值,从函数返回值这些操作不会复制值,而是转移(move)值,将原来值所有者的所有权让渡给目标所有者。对于原来的所有者将变成不可访问状态。这也是 Rust 与其它语言的不同之处,如果你能理解 move,那明你已经掌握了 Rust 三分之一的内容了。所有权机制仅仅是针对在堆上分配数据的类型。下面将对这三个场景展开详细介绍。


0x03 转移——变量之间赋值


先展示个基本类型和 String 类型变量间赋值的示例吧。示例代码如下:

fn main() {
    // 基本数据类型
    let a = 3;
    let b = a;
    println!("a = {}, b = {}", a, b);
    // String类型
    let m = String::from("rust");
    // 让渡所有权
    let n = m;
    // m 变为无法访问,下面的代码将会报错
    // println!("m = {}, n = {}", m, n);
    println!("n = {}", n);
    // 变量遮蔽
    let m = 5;
    println!("m = {}", m);
}
// 运行结果
// a = 3, b = 3
// n = rust
// m = 5

当我打开 println!("m = {}, n = {}", m, n); 这行代码的注释,编译将出现下面的错误。


0a2653c851af460fa595bd959398a8f1.png


蓝字大致意思:因为变量 m 是一个 String 类型,该类型并不会实现 Copy trait,在这里发生了 move 。发生转移的代码是 let n = m。一旦发生转移,变量 m 将无法访问。当然我们可以通过变量遮蔽的特性,重新为 m 赋新值。


下面我将通过内存分析与 Python、 C++ 比较,Rust 与他们的不同之处。

假设有一个列表 a, 包含三个字符串 "hello", "study", "rust"。将此列表赋值给 b,再赋值给 c。

Python


Python 代码如下:
a = ['hello', 'stduy', 'rust']
b = a
c = a


2d65d23f6d4748949b924e4057485923.png


Python变量赋值内存简易模型


上图是 Python 变量赋值内存简易模型,忽略了一些非必要字段。由图可见,每一个 Python 对象都存在一个引用计数,用来记录当前引用此值数量。上图左边表示变量 bc 赋值之前,内存中的列表的引用计数为1。当被赋值之后(上图右侧标红显示),Python 对于变量间赋值,是创建一个指向对象的新引用,然后增加引用计数。


C++


我们再来看下 C++,代码如下:

vector<string> a = {"hello", "study", "rust"};
vector<string> b = a;
vector<string> c = a;


6de278e6d6694ce5bb08e7e842b7e74b.png


C++变量赋值内存简易模型


上图是 C++ 变量赋值内存简易模型,忽略了一些非必要字段。赋值前(上图左边)栈上保存指向堆上数据的指针。赋值后(上图右边)内存中会出现 3 个 vector 和 9 个字符串。由上图可得,C++ 的赋值是采用了深复制的做法。显而易见 C++ 的赋值代价是比较大的,可能会消耗较多的内存和处理器时间。但是它的优点就是所有权非常清晰,在变量超出作用域,我们就可以直接释放内存。


Rust


最后再来看 Rust,Rust 则采用了相反的策略,这个在本篇文章开始已经讲过了,现在来看下内存模型吧。示例代码如下:

let a = vec![String::from("hello"), String::from("study"), String::from("rust")];
let b = a;
// 下面的语句会报错
// let c = a;

Rust 变量赋值内存简易模型


经过 let b = a 后,把向量 a 的三个字段转移到了 b 上,b则拥有了这个向量,向量原有在堆上的数据并没有移动。向量的所有者还是只有一个。原有的向量 a 则不能被使用,编译器认为其没有被初始化。这个过程没有发生堆上的数据移动,也没有引用计数,代价非常小。这种方式有没有发现很巧妙呢,哈哈。


0x04 小结


Rust 通过与众不同的方式确保了内存安全,所有权系统也是 Rust 的核心功能。文章通过对比 PythonC++ ,其实也是对比了两种方式,目前主流虚拟机语言大部分都采用引用计数器方法。其实语言内存的管理方法都各有优劣,并不能说 Rust 转移这种做法就没有缺点,比如使用 Rust 来实现很多数据结构有难度等等,毕竟为了性能总需要有点儿取舍。希望你看完本篇文章能对 Rust 有一个新的认识。

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