【Rust中级教程】1.9. 所有权(简单回顾):所有权的核心思想、如何实现`Copy` trait、值的删除(丢弃)、值删除的顺序
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
这篇文章只对所有权进行简单回顾,想要看完整的所有权系统阐述见 【Rust自学】4.2. 所有权规则、内存与分配。
1.9.1. 所有权的核心思想
Rust内存模型的核心思想是所有值都只有一个所有者。也就是说只有一个位置(通常是作用域来) 负责释放每个值。
这种效果是通过借用检查器实现的,如果值移动了(赋值新变量、推到Vec
上、置于堆内存上等),其所有者也变成新的位置了。
所有者实际上就是内存上的一个位置,数据所在的位置就是值的所有者。移动指的是数据从一个位置转移到另一个位置,新的位置就是数据的所有者。
但是有些类型不执行这种规则:如果值的类型实现了Copy
trait,那么在重新赋值时不是移动而是复制。也就是把值复制一份放到新的位置。
1.9.2. 如何实现Copy
trait
实现Copy
trait的类型必须可以按位(bit)来复制值。
能实现Copy
trait的类型自然不包括:
- 含有
non-copy
类型的类型 - 如果一个类型在其值被丢弃时,必须执行某些特殊的资源释放操作
为什么呢?
想象一下,如果Box<T>
实现了Copy
trait,进行赋值:box1 = box2
,这时候这两个变量都认为自己在堆内存上有一块专属于自己的空间,所以当它们走出作用域时,它们都会尝试释放那块内存,导致双重释放(double free),其危害在 【Rust自学】4.2.5. 内存和分配 中已作介绍,可以点击链接查看,这里不做重复。
1.9.3. 值的删除(丢弃)
当值不再被需要时,其所有者会将其删除。
值的删除(或者叫丢弃)发生于值走出作用域时。类型会递归地将其所包含的值删除。比如说我们要删除一个复杂类型的变量,会导致需要删除很多值。
但Rust不会发生多次删除同一个值的情况(因为所有权设计)。变量若含有对其他值的引用(不拥有该值),当变量被删除时,其它的值不会被删除。
这样说可能难理解,那我们看一个简单的例子:
fn main() {
let x1 = 42;
let y1 = Box::new(x1);
{
let z = (x1, y1);
}
let x2 = x1;
}
x1
是i32
类型,y1
是Box<i32>
类型,指向x1
- 使用
{}
创建了一个新的作用域,在这个小作用域里创建了z
z
是元组类型,其值是(x1, y1)
,x1
是i32
类型,实现了Copy
trait,所以x
是把自己的值复制了一份给z
;y1
是Box<i32>
,没有实现Copy
trait,所以它不能复制值,而是把所有权转移给了z
- 离开了小作用域之后又使用了一次
x1
,x1
此时还保持有效在于x
是把自己的值复制了一份给z
,自身仍然保持有效。 - 而
y1
在给z
赋值之后就失效了,因为它把所有权转移给了z
1.9.4. 值删除的顺序
-
变量(包括函数的参数)按照相反的顺序进行删除。
在上面的例子中,y1
是先声明的,z
是后声明的。所以在删除时会先删z
再删y1
-
嵌套的值按照源代码的顺序进行删除。
在上面的例子中,z
在被删除时会按照先删除x1
,再删除y1
的顺序
注意:Rust暂时不允许在单个值内进行自我引用