Rust学习(二):所有权
1 栈和堆
现代处理器在内存中跳转越少就越快
栈内存(所有存放在栈内存的数据必须有已知的固定的大小)
- 因为放入栈的数据大小都是固定的,因此入栈的时候很快,因为挨着放就行了
堆内存(heap)
- 编译时大小未知,或者程序运行时会变化的数据存放在
堆 - 堆对于内存的组织性差一些,因为大小不固定,或者会改变,因此,需要分配空间,而这就需要操作系统去找到一块足够大的空间
- 访问数据的时候也更慢,因为有个寻址的过程
函数调用
在调用函数的时候,会将函数的入参传入函数,包括入参的指针信息,然后,函数的这些参数信息会压栈,等执行完成会出栈
2 所有权的规则
所有权存在的原因就是因为上面的堆内存,因为这块是不确定的,有的是需要回收的。
所有权(系统)是 Rust 最为与众不同的特性,对语言的其他部分有着深刻含义。它让 Rust 无需垃圾回收(garbage collector)即可保障内存安全,因此理解 Rust 中所有权如何工作是十分重要的。本章,我们将讲到所有权以及相关功能:借用(borrowing)、slice 以及 Rust 如何在内存中布局数据
- 每个值都有一个变量,这个变量就是该值的所有者
- 每个值,同时只能有一个所有者
- 当所有者超出作用域的时候,这个值会被删除
定义一个字符串hello,其内存结构是这样的

而当将携带此字符串值的变量s1赋值给s2的时候,s1在栈中就不可用,失效了,不同于浅拷贝,这种方式被称为移动(Move)
而如果需要实现深拷贝的功能的话,则需要实现 clone方法,类似java
而针对栈上面的数据比如基本类型:整数,布尔,浮点数,字符等是放在栈上的,不需要拷贝这些东西,因此引出了 复制的概念:
1 | let x = 5 |
rust提供了 Copy trait(复制 接口)
- 如果一个类型实现了这个接口,那么旧的变量在值茶u年底后还是有效的,类似上面的代码
- 如果一个类型,或者这个类型的一部分实现了
Drop trait(释放 接口),那么这个类型就不允许去实现Copy trait(复制 接口)了
实现了复制接口的有:
- 整数类型,布尔,字符,浮点,Tuple(这个元组内的所有类型都是实现了复制接口)
2. 1 所有权和函数
将值赋给函数或者变量,在语义是其实是一样的:
1 | fun main(){ |
一个变量的所有权总是符合这样的模式:
- 把一个值赋给变量的时候就会发生移动(Move)
- 包含这个堆数据的变量离开作用域,值就会被drap函数清理,除非数据的所有权移动到另一个变量了
如何让,一个函数使用一个值,但是不拥有所有权呢?
- 简单但麻烦的方式,就是返回一个元组,将入参原封不动的返回
- 使用Rust的特性
引用(Reference)
2.2 引用
使用 &符号,表示使用这个值,但是不获得其所有权
1 | fn main(){ |
这段代码在内存中的形式是这样的:
- s表示一个指针,这个指针指向另一个指针s1,s1指向堆内存

而引用的内容,是否可以修改呢?,我们尝试在上面的方法内修改变量,会报如下的错:
s.push_str(“, World”);
| ^^^^^^^^^^^^^^^^^^^^^sis a&reference, so the data it refers to cannot be borrowed as mutable(S是引用,所以它引用的数据不能作为可变数据来借用)
因为引用,也是存放在栈上的,和变量一样是不可变的,而变量可以通过 mut来表示可变变量,那引用呢?答案是可以的:
1 | fn main(){ |
1 可变引用
可变引用的限制:
- 在一个作用域内,对于某块数据,只能有一个可变引用
这样做的好处有就是在编译的时候防止 数据竞争,以下三种情况都满足的情况会发生数据竞争
- 多个指针同时访问一个数据
- 至少有一个指针用于写入数据
- 没有任何机制来同步对数据的访问
但是我们可以通过创建作用域的方式,来允许非同时创建多个可变引用
1 | fn main(){ |
还有一个限制:
不可以同时拥有一个可变引用和不可变引用
2 悬空指针
悬空指针(Dangling Pointer),或者叫野指针,指向内存一个地址,但是这块内存已经被回收了,甚至已经分配给别人了,而Rust永远不会出现这个问题
- Rust保证了,在引用一个数据的时候,编译器会保证,在引用离开作用域之前,数据不会离开作用域
1 | fn main(){ |
上面的代码编译会报错,因为str在方法结束就销毁了,而str的引用却返回了,也就是出现了野指针,这样在rust编译会不通过的
3 切片
Rust的一种 不持有所有权的数据类型,英文slice,可以用于解决下面的问题
小例子
返回第一个空格的下标,这里其实是有问题的,因为,这个index是不合字符串s绑定的,表示的只是之前算过的值,而不是当前的值,可以看到仅仅是返回一个空格下标这么多代码,还有bug
1 | fn main(){ |
字符串切片
左闭右开的语法:以及部分语法糖
1 | fn main(){ |
字符串切片在内存中的结构,指向堆内存数据的一部分

改造之前的小例子,使其返回一个字符串切片,字符串切片的类型标识为:&str
1 | fn main(){ |
字符串字面值
字符串字面值,是什么类型的呢,没错就是切片
将切片作为参数
之前的例子都是字符串引用作为入参:
1 | fn first_world(s: &String) ->&str{ |
其实,可以使用切片作为入参,这样切片可以作为入参,二字符串可以传入完整的切片,入参更为多样了。因为切片还是一种引用嘛,一样的
- 标题: Rust学习(二):所有权
- 作者: chalmery
- 创建于 : 2022-05-14 00:00:00
- 更新于 : 2022-05-14 00:00:00
- 链接: https://github.com/chalmery/2861842578/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。