所有权
大约 3 分钟
栈内存(Stack) & 堆内存(Heap)
Stack 按值的接受顺序来储存,按相反的顺序将它们移除(后进先出 LIFO,LastIn FirstOut)
- 添加数据叫做压入栈(压栈)
- 移除数据叫做弹出栈(出栈)
所有储存在 Stack 上的出局必须拥有固定一致的大小
- 编译时大小未知的数据或运行时大小可能发生改变的数据必须存放在 Heap 上
Heap 内存组织性差一些
- 当数据放入 Heap 时,程序将请求一定数量的空间
- 操作系统在 Heap 里找到一块足够大的空间,把它标记为
在用
,并返回一个指针,也就是这个内存的空间地址 - 这个过程叫做在 Heap 上进行分配,有时仅仅称之为
分配
存储数据
- 把值压到 Stack 上不叫分配
- 指针是已知固定大小,可以把指针存放在 Stack 上
- But,如果想访问实际数据,则必须使用指针进行定位
- 把数据放到 Stack 上比放在 Heap 上要快得多,因为操作系统不需要寻找用来储存数据的空间,那个位置永远在 Stack 的顶端
访问数据
- 访问 Heap 中的数据要比访问 Stack 中的数据慢,因为需要用过指针才能找到 heap 中的数据。对于现代的处理器来说,指令在内存跳转的次数越少,,熟读就越快
- 如果数据放的距离比较近,那么处理速度就会快一些(Stack 上)
- 如果数据放的距离比较远,那么处理速度就会慢一些(Heap 上)
函数调用: 当你的代码调用函数时,值被传入函数(也包括指向 heap 的指针). 函数本地的变量被压到 Stack 上. 当函数结束后,这些值会从 Stack 上弹出
所有权
所有权解决的问题:
- 跟踪代码哪些部分正在使用 heap 的哪些数据
- 最小化 heap 上的重复数据量
- 清理 heap 上未使用的数据以避免空间不足
使用所有权不需要去担心 Stack 和 Heap
管理 Heap 数据是所有权存在的原因
所有权规则
- 每个值都有一个变量, 这个变量是该值的所有者
- 每个值同时只能有一个所有者
- 当所有者超出作用域(Scope)时, 该值将被删除
变量作用域
- 作用域(Scope)是程序中一个项目的有效范围(以声明开始,以
}
结束)
fn main(){
//s不可用
let s = 5; //s可用
//可以对s进行操作
}//s的作用域到此结束
内存分配
- 当变量走出作用域时,内存会立即自动交还给操作系统
- Drop 函数
变量数据交互的方式
- Move 移交
- move 会将所有权移交(位于堆上的数据)
let s1 = String::from("hello"); let s2 = s1; //此时s1的数据移动到了s2
- move 会将所有权移交(位于堆上的数据)
- clone 克隆
- 在堆上的数据
let s1 = String::from("hello"); let s2 = s1.clone();
- 在栈上的数据
let x = 5; let y = x;
- 整形实现了`copy trait`: 一个旧的变量将其值赋值给新变量后任然可用