所有权¶
解决的问题¶
- 跟踪代码的哪些部分正在使用 heap 的哪些数据
- 最小化 heap 上的重复数据
- 清理 heap 上未使用的数据以避免空间不足
所有权规则¶
- 每个值都有一个变量,这个变量是该值的所有者(owner);
- 每个值同时只能有一个所有者;
- 当所有者超出作用域(scope)时,该值将被删除。
作用域
所有权变动¶
所有权变动 会发生在 2 种情况下:
-
把一个值赋给另一个变量时;
-
函数传参和返回
所有权变动分为 2 种情况: 移动
和 拷贝
。
所有的变量都是栈上数据的一个别名,简单标量类型数据保存在栈上,复杂类型则在栈上保存一份数据结构,实际的数据存放在堆上。
对于所有权的变动,主要是在栈到堆上的区别。
-
移动 Move
默认的变动行为是:移动 Move。所有权被移动后,原来的变量就不能再使用了。
即所有权被移动后,原先的变量在栈上的数据结构就变回未初始化的状态。
-
拷贝 Copy
另外一种变动行为是:拷贝 Copy。所有权不会被转移,而是把值拷贝了一份。
其实就是新的变量和原有的变量,在栈上的这两份数据结构,她们指向了同一份堆上的数据。
-
克隆 Clone
克隆需要显式的实现 Clone trait,并且显式的调用
.clone()
方法。克隆不仅栈上有新的变量和原有变量两份数据,而且堆上的数据也是各自一份。
克隆不会导致原有变量失去所有权。因为克隆是实实在在复制了一份。
- 如果类型 没有实现 Copy trait,则所有权发生转移时执行的是移动 Move
- 如果类型 实现了 Copy trait,则所有权发生转移时执行的是复制 Copy(浅拷贝)
- 如果类型 实现了 Clone trait,则显示调用 .Clone() 会复制一份堆内存(深拷贝)
- 实现了 Clone trait,在变量离开作用域时会自动调用 Drop 函数
- 实现了 Copy trait 就不能实现 Drop trait.
关于 Copy:
- 任何简单标量的组合类型都可以是 Copy 的
- 任何需要分配内存或某种资源的都不是 Copy 的
- 一些拥有 Copy trait 的类型:
- 所有整数类型,例如 i32, u64等
- 所有浮点类型,例如 f32, f64
- bool
- char
- Tuple(前提是Tuple中的类型都是 Copy 的),例如 (i32, f64)
- 引用,例如
let p1 = &s; let p2 = p1;
p1 是一个引用,赋值给 p2 的时候执行的是 Copy。
Note
如果没实现 Copy trait,则默认为 Move。如果实现了 Copy trait,则默认为 Copy。
实现了 Copy 就不能实现 Drop,实现了Clone 就可以实现 Drop。
简单标量类型及其组合默认实现了 Copy trait,默认分配在栈上。
借用¶
在其他语言中,函数传参是将值复制了一份传递过去,源数据依然可以继续使用。
但是 Rust 的所有权概念非常严格,传了参就失去了所有权,想要再次获取还得函数返回出来。
所以为了方便,在 Rust 中就有了「借用」的概念。
- 引用 Reference :
&
符号表示引用,允许你引用某些值而不取得其所有权。(引用默认情况下也不可变) - 借用 Borrow :把引用作为函数参数的这个行为,就叫做借用。
- 借用分为 共享借用(不可变引用) 和 独享借用(可变引用)。
共享借用(不可变引用)
独享借用(可变引用)
要注意一个数据不可以同时被可变引用(独享借用)两次,这是为了防止数据竞争。