Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1.9 所有权(简单回顾):所有权的核心思想、如何实现 Copy trait、值的删除(丢弃)、值删除的顺序

这篇文章只对所有权进行简单回顾。

1.9.1. 所有权的核心思想

Rust内存模型的核心思想是所有值都只有一个所有者。也就是说只有一个位置(通常是作用域来) 负责释放每个值。

这种效果是通过借用检查器(详见 1.11.2. 借用检查器(Borrow Checker))实现的,如果值移动了(赋值新变量、推到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. 所有权规则、内存与分配 中已作介绍,可以点击链接查看,这里不做重复。

1.9.3. 值的删除(丢弃)

当值不再被需要时,其所有者会将其删除。

值的删除(或者叫丢弃)发生于值走出作用域时。类型会递归地将其所包含的值删除。比如说我们要删除一个复杂类型的变量,会导致需要删除很多值。

但Rust不会发生多次删除同一个值的情况(因为所有权设计)。变量若含有对其他值的引用(不拥有该值),当变量被删除时,其它的值不会被删除。

这样说可能难理解,那我们看一个简单的例子:

fn main() {  
    let x1 = 42;  
    let y1 = Box::new(x1);  
  
    {  
        let z = (x1, y1);  
    }  
      
    let x2 = x1;  
}
  • x1i32类型,y1Box<i32>类型,它拥有堆上的一块分配,里面存的是x1值的副本(因为i32实现了CopyBox::new(x1)并不会借用x1
  • 使用{}创建了一个新的作用域,在这个小作用域里创建了z
  • z是元组类型,其值是(x1, y1)x1i32类型,实现了Copy trait,所以x1是把自己的值复制了一份给zy1Box<i32>,没有实现Copy trait,所以它不能复制值,而是把所有权转移给了z
  • 离开了小作用域之后又使用了一次x1x1此时还保持有效,因为x1是把自己的值复制了一份给z,自身仍然保持有效。
  • y1在给z赋值之后就失效了,因为它把所有权转移给了z

1.9.4. 值删除的顺序

  • 变量(包括函数的参数)按照相反的顺序进行删除。
  • 嵌套的值按照源代码的顺序进行删除。在上面的例子中,当离开内部作用域时会删除z:它先删除第一个元素(那个被复制的i32),再删除第二个元素(那个Box)。因为y1的所有权已经移动进z,之后不会再单独删除y1

注意:Rust暂时不允许在单个值内进行自我引用