Rust的所有权

栏目: 编程语言 · Rust · 发布时间: 5年前

内容简介:作为一个Rubyist,我所知道的关于内存分配的所有内容都是由一些称为垃圾收集的process处理的,这是因此,当我打开处理记忆管理的责任是否应该堆积在我身上?

作为一个Rubyist,我所知道的关于内存分配的所有内容都是由一些称为垃圾收集的process处理的,这是 Aaron Patterson 的问题,而不是我的问题。

因此,当我打开 Rust Book 并看到Rust的一个定义功能是垃圾收集的替代品时,我变得有点担心。

处理记忆管理的责任是否应该堆积在我身上?

显然,对于像C这样的其他系统编程语言来说,处理内存分配是一件大事,如果做得不好就会产生重大影响。

Stack(栈) & Heap(堆)

堆栈和堆是在运行时管理内存的方法。

Rust的所有权

堆被认为是快速的,因为它有序地存储和访问数据。栈顶是在栈中添加、删除数据的唯一位置。这被称为LIFO,后进先出,意味着我们在释放内存时只需知道栈顶的地址。

栈快速的另一个原因,栈所需的内存空间在编译时是已知的。这意味着我们可以在将数据存储到其中之前分配一块固定大小的内存。

例如,如果你知道有四个人参加您的晚宴,你可以提前决定每个人的座位,准备多少食物,并在他们到达之前练习他们的名字。这是超级高效的!如果你不能提前确切地知道有多少人参加您的晚宴,你可以使用堆。使用堆意味着需要准备足够多但未知数目的椅子并向到来的人发出名称标签(name字段)。

当在运行时期间需要存储未知大小的数据时,计算机会在堆上搜索内存,对其进行标记并返回一个指针,该指针指向内存中的位置。这称为分配内存。然后,您可以在栈上使用该指针,但是,当你要检索真实数据时,需要读取指针指向的内存位置的数据。

当我不断深入了解栈和堆时,管理堆中的数据会很困难。例如,你需要确保在完成使用一块内存后允许计算机重新分配这块内存。但是,如果其中一个代码块释放了内存中的某个位置,而另一个代码块仍然持有该内存的指针,则会出现 悬空指针(dangling pointer)

跟踪哪部分代码正在使用堆上的哪些数据,最小化堆上的数据拷贝,以及清理堆上未被使用的数据使其不会耗尽内存空间,这是所有权解决的所有问题。

Rust Book

Ownership(所有权) & Scope(作用域)

Rust关于所有权的三个规则:

Rust中的每个值都有一个称为其所有者的变量名。(let name = "xxx")

同一时间只能有一个所有者。

当所有者超出作用域时,该值将被删除。

所有权的最简单的例子是关于变量的作用域:

Rust的所有权

一旦当前函数作用域结束,由}表示,变量hello超出作用域会被删除。这一点和大多数编程语言的本地变量是一致的。但这不是所有权的全部,当我们需要在传递值,并将存储在栈中的字符串常量切换到存储在堆上的String类型时,事情会变得更有趣。

Rust的所有权

当使用字符串常量时,正如我们所期望的那样,Rust将hello的值复制到hello1中。 但是当使用String类型时,Rust会移动该值。编译时会抛出错误:error[E0382]: use of moved value: 'hello’。

看起来在使用字符串常量时,Rust会将该一个变量的值复制到另一个变量中,但是当我们使用String类型时,它会移交所有权。关于这个话题在论坛里有相关讨论,请阅读此贴子 The Copy trait - what does it actually copy? .

以下是我对讨论后的总结:

Rust的所有权

字符串常量“Hello,World!”存储在只读内存中的某个位置(既不在栈中也不在堆中),并且指向该字符串的指针存储在栈中。 这里的指针通常是称为引用,这意味着我们使用指向存储在永久内存中的字符串常量的引用(参见 Ownership in Rust, Part 2 中有关引用的更多信息),并保证它在整个程序的运行时间里是有效的(它有一个静态的生命周期)。

变量hello和hello1存储在栈。 当我们使用=运算符时,Rust会将存储在hello中的指针值的副本绑定到变量hello1。 在作用域的最后,Rust会调用drop方法从栈中删除变量以释放内存。 这些变量可以存储并轻松地在栈中进行复制,因为它们的大小在编译时是已知的。

Rust的所有权

在堆上,字符串类型的值为“hello,world!”使用 string:from 方法绑定到变量hello。但是,与字符串常量不同,绑定到变量hello的是数据本身而不仅仅是指针,并且这些数据的大小可以在运行时更改。=运算符将变量hello指向的数据绑定到新变量hello1,有效地将数据的所有权从一个变量移交给另一个变量。变量hello现在是无效的,根据所有权规则2:“同一时间只能有一个所有者。”

但为什么这样呢?为什么Rust不始终复制数据并将其绑定到新变量?

回想一下栈和堆之间的差异,堆上存储的数据大小在编译时是不可知的,这意味着我们需要在运行时进行一些内存分配步骤。这可能会代价很高。根据我们的数据量,如果我们整天都在copy数据,可能会很快耗尽内存。除此之外,Rust的默认行为会保护我们免受内存问题的影响(可能在其他语言中遇到)。

将数据存储在堆上并在栈上存储指向该数据的指针。但是,与使用指针指向只读内存(存储字符串常量)不同,堆上的数据可能会发生变化。指针值<< DATA >>绑定到存储String类型的hello变量。如果我们将相同的指针值绑定到两个不同的变量,看起来像这样:

Rust的所有权

我们有两个变量hello和hello1,它们共享相同值的所有权。 这违反了规则2:“同一时间只能有一个所有者”,但让我们继续。

在变量hello和hello1的作用域结束时,我们必须将他们在堆上的内存释放。

Rust的所有权

首先,我们将hello1指向的堆上内存数据释放,现在当我们释放hello时会发生什么?

Rust的所有权

这称为双重释放错误(double free error),我认为在这个StackOverflow答案中有最好的总结: https://stackoverflow.com/a/2...

A double free in C, technically speaking, leads to undefined behavior. This means that the program can behave completely arbitrarily and all bets are off about what happens. That’s certainly a bad thing to have happen! In practice, double-freeing a block of memory will corrupt the state of the memory manager, which might cause existing blocks of memory to get corrupted or for future allocations to fail in bizarre ways (for example, the same memory getting handed out on two different successive calls of malloc).

Double frees can happen in all sorts of cases. A fairly common one is when multiple different objects all have pointers to one another and start getting cleaned up by calls to free. When this happens, if you aren't careful, you might free the same pointer multiple times when cleaning up the objects. There are lots of other cases as well, though.

templatetypedef

Rust就是要避免犯这类错误。通过使hello无效,编译器知道只在hello1上发出一个释放内存的调用(drop)。

这一切都很好,但有些情况下我们确实想要copy存储在堆中的数据。 Rust中可以使用 clone()方法 实现。

Rust的所有权

请记住,调用clone()的代价可能会很高,这就是Rust默认阻止这类“deep copy”的原因。

显然,Rust的所有权涉及的知识还有很多: 借用(borrowing),引用(referencing)和切片(slicing)!

后续补充......

参考


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户故事与敏捷方法

用户故事与敏捷方法

Mike Cohn / 石永超、张博超 / 清华大学出版社 / 2010-4 / 39.00元

《用户故事与敏捷方法》详细介绍了用户故事与敏捷开发方法的结合,诠释了用户故事的重要价值,用户故事的实践过程,良好用户故事编写准则,如何搜集和整理用户故事,如何排列用户故事的优先级,进而澄清真正适合用户需求的、有价值的功能需求。 《用户故事与敏捷方法》对于软件开发人员、测试人员、需求分析师和管理者,具有实际的指导意义和重要的参考价值。一起来看看 《用户故事与敏捷方法》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具