深入浅出Rust所有权:手把手从零设计Rust所有权体系,掌握Rust内存管理思想的精髓

撰写编程语言发展历史过程中,对Rust的所有权机制的设计进行了深入的探讨,摘取其中的一段内容,邀请大家点评。

Rust的所有权机制,看似复杂且与现有编程语言不同,使用起来思路也许难以适应。是学习Rust的难点。但如果我们换个思路,假设我们是Rust的设计者,逐步深入Rust的内心世界,也许更容易掌握Rust所有权的思想和用法。

历史难题

在软件工业界,内存管理一直是一个棘手的问题。主要有两大问题尚未解决:

  1. 内存泄露
  2. 垂悬指针

编程语言探索了多种内存管理方式,但都未能彻底解决这两个问题。例如,自动垃圾回收能够很好地解决垂悬指针问题,但它不能完全避免内存泄露,更不用说手动内存管理或引用计数了。

是否有可能设计一种机制,能够彻底解决内存泄露和垂悬指针的问题呢?

开始设计

让我们首先考虑,导致内存泄露和垂悬指针的根本原因是什么?

  1. 内存泄露的原因:循环引用、使用后未释放
  2. 垂悬指针的产生:某处释放了指针,而另一处还尝试读写

如果我们设计一种机制,禁止开发者编写出含有“循环引用”和“同时存在多个读写引用”的代码,并提供“使用后自动释放”的机制,是不是就能彻底解决这个问题了?

在现有编译器的能力下,提供使用后自动释放的机制并不困难。我们可以规定一种机制,这种机制可以追踪和管理每个值的内存,并确保当一个值不再被需要时,它能够及时被销毁。

接下来,我们重点关注“循环引用”和“同时存在多个读写引用”。

“循环引用”的本质是所有权不明确。为了避免这种情况,我们需要经常地将所有权转移,于是我们决定这样规定:

  1. 所有权可以转移,并且需要经常转移。
  2. 当一个值从一个变量赋值给另一个变量时,所有权就从这个变量转移到了另一个变量。
  3. 新变量拥有了这个值的所有权,旧变量失去了这个值的所有权,旧变量无效了。
  4. 每个值都有一个变量作为其所有者,且在任意时刻只能有一个所有者。

通过所有权的概念,结合及时销毁的原则,我们需要引入生命周期(lifetimes)的概念,对这些规定进行准确的定义。

生命周期(lifetimes):标识了从创建(或绑定)到销毁(或解绑)的整个时期,是值、变量或引用在内存中存在的时间段。

结合生命周期(lifetimes)概念,我们可以将追踪和管理内存的规则优化为:

  • 值的生命周期(lifetimes)在其所有权拥有者离开其作用域时结束,值及其占用的内存都将被释放。

但是,在现实世界的编程中,我们通常需要多个变量来引用同一个值。在现有的概念框架下,就需要共享所有权,但共享所有权打破了我们“任意时刻只能有一个所有者”的规则,重新导致循环引用问题出现。为了遵循前面我们制定的已有规则,同时允许其他变量访问这个值,我们需要对所有权概念进行细化,增加一些限制,引入了借用(borrowing)概念。

借用不拥有值的所有权但是可以访问值

借用不会增加值的生命周期,因此即使在循环引用的情况下,生命周期也不会被无限延长,从而避免了内存泄露。

到目前为止,这套机制已经能够很好地解决内存泄露问题。但还有一个“垂悬指针”问题待解。垂悬指针的根本问题在于存在多个可读写引用,当一处释放了内存,而另一处还在尝试访问时,就会出现问题。

我们需要一套规则来避免同时存在多个可读写引用。如果一个值有多个可读引用,这是安全的,但一旦出现一个可写引用,无论是读还是写,都是危险的。因此,我们需要进一步细化借用,并制定以下规则:

  1. 借用分为可变借用和不可变借用。可变借用产生的是可变引用的变量,不可变借用产生的是不可变引用的值。
  2. 如果一个值存在一个可变引用(&mut),那么它必须是唯一的。不能同时存在其他的可变引用或不可变引用。
  3. 如果一个值存在一个或多个不可变引用(&),则不能同时存在可变引用。

2和3的规则,和读写锁的规则很像。可见编程世界里的很多概念有其内在的相通之处。

通过新引入的规则,确保了在任何给定的时间,要么只有一个可变引用对值进行修改,要么有多个不可变引用进行读取,但不允许同时进行读写。这样一来,就能避免数据竞争,防止垂悬指针的产生。通过这套规则,我们有效地解决了“垂悬指针”的问题。

至此,我们再次梳理并总结一下我们之前制定的规则:

  1. 所有权决定生命周期。
  2. 每个值只能有一个所有权拥有者。
  3. 所有权可以转移。
  4. 借用允许创建指向值的引用。
  5. 借用分为可变引用和不可变引用。
  6. 可变引用(&mut)必须是唯一的。不能同时有其他的可变引用或不可变引用存在。
  7. 不可变引用(&)可以存在多个,但不能和可变引用(&mut)共存。
  8. 编译器确保所有借用值的作用域不超过其值的生命周期。
  9. 编译器确保每一个值的所有权者离开作用域时,销毁所拥有的对象。

举个栗子

为了加深理解,我们用一个现实生活中的场景来类比思考:
假设我们正在组织一场音乐会,音乐会上的乐器代表程序中的数据。在音乐会上,每个乐器(数据)在任何时刻只能由一位乐手(变量)负责演奏(拥有)。这位乐手对乐器有完全的控制权,可以决定何时开始演奏、何时停止,以及如何演奏。这个约定是所有权概念的体现:一段数据在任何时刻只有一个所有者。
现在,如果这位乐手需要休息,需要将乐器留在了原地,其他乐手可以来尝试演奏这个乐器,但必须遵守一些规则:

  • 如果一位乐手只是想试听乐器的声音,不打算改变任何设置(即只读访问),那么可以有多位乐手同时这样做。这就像不可变借用(&T),允许以只读方式借用数据。

  • 如果另一位乐手想要调整乐器(即写入访问),比如调整琴弦的松紧,以便以不同的风格演奏,他们必须等到没有其他人在试听乐器时才能进行。这是可变借用(&mut T),它确保在可变借用期间没有其他的借用。

  • 一旦调整乐手完成他们的调整并将乐器交回,原来的乐手可以继续演奏。这是当可变引用结束时,原始数据的所有权会返回给所有者。

  • 如果原来的乐手决定不再演奏,他们可以将乐器传递给另一个乐手,使得新的乐手成为乐器的新所有者。这可以看作是所有权转移。一旦所有权转移发生,原所有者将无法再访问那段数据。

  • 为确保音乐会上的每件乐器在整个活动结束时都能得到妥善管理,组织者(即编译器)会跟踪每个乐器的负责人,并确保在音乐会结束时,所有乐器都已被妥善回收。这样就避免了“忘记的乐器”(内存泄漏)或演奏中的混乱(垂悬指针)。

在这个类比中,组织者确保乐器的使用遵循一套明确的规则,预防混乱和潜在的冲突。类似地,在我们设计的内存安全的规则中,编译器保证内存访问遵守所有权、借用和生命周期的规则,防止数据竞争和无效访问,从而确保程序的安全性。

完善体系


实际上,Rust还需要一些其他附加的规则,让这套规则在实际的编程场景中更加好用和更安全。这些规则包括:

  1.  一般语言的赋值运算符(=)、参数传参默认的语义是复制,而Rust默认的赋值语义是所有权转移(移动语义)(对于实现了Copy特性的类型(例如基本数值类型),Rust默认使用复制语义),这个区别是Rust初学者需要首先适应的思维差异。
  2. 一些特殊情况下,编译器无法准确判断对象生命周期,因此引入了生命周期标注来让编译器知道引用的有效范围。
  3. 变量默认是不可变的,这样更容易创建不可变引用,避免不必要的可变引用导致编程的限制增多。因此可变引用必须通过mut显式地声明可变。


到这里,我们设计给规则,基本涵盖了Rust的所有权体系中核心的理念和规则了。

Rust通过所有权体系的这些规则和限制,成功地创建了一种能够在编译时强制执行内存安全的编程语言,并且无需依赖运行时的垃圾回收器(影响运行时性能)。这使得Rust非常适合系统编程,为构建高性能应用程序提供了强大的工具。

在我们设计的过程中,我们增加了许多核心规则都限制了变量的访问场景,我们可以隐约感受到这套规则使用起来确实会和已有的编程语言有很大的差别,甚至这些限制可能会给开发者造成一定的不便。但是这些核心规则虽然增加了学习的难度,但一旦掌握,它们将使程序员能够编写出更安全、更高效的代码。

随着Rust生态系统的不断成熟,以及社区提供的丰富资源和工具,Rust越来越受欢迎,并在各种领域得到应用。

但是,并发编程(多线程编程)中存在的内存访问冲突(也称为内存竞争)的问题,仍然是编程界另一大难题,读到这里的读者,相信对技术非常有想法,也许下一个解开这个难题的人就是你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/847545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回归现实:无需复杂假设即可轻松评估过程能力的简单方法

Cpk 和 Ppk 等过程能力指标能够测量您的过程相对于客户规格要求的执行情况。我们先回顾一些能力分析基础知识,再深入了解另一个能力估计值 Cnpk,该估计值很可能在您的能力分析库中非常有用。 能力统计指标分析 能力统计指标使用单个数字,是一…

什么是最好的手机数据恢复软件?6 款手机数据恢复软件 [2024 年更新]

什么是最好的手机数据恢复软件?在这篇文章中,您将了解 6 款最好的免费手机数据恢复软件,并学习如何恢复数据的完整指南。 最好的手机数据恢复软件是什么? 手机数据恢复软件是恢复智能手机中丢失或删除的文件、消息、照片和其他宝…

Python入门课堂:探索编程世界的无限可能

Python入门课堂:探索编程世界的无限可能 在数字化时代的浪潮中,Python以其简洁易懂的语法和强大的功能库,成为了众多编程初学者的首选语言。本篇文章将带领大家走进Python的世界,从四个方面、五个方面、六个方面和七个方面全面解…

运筹学_8.决策论

引言 决策论是根据信息和评价准则,用数量方法寻找或选取最优决策方案的科学,是运筹学的一个分支和决策分析的理论基础。在实际生活与生产中对同一个问题所面临的几种自然情况或状态,又有几种可选方案,就构成一个决策,…

GaN功率电子器件中体缺陷相关机制的建模仿真研究

在电力电子器件的外延生长和器件制备过程中,缺陷是不可避免的,大量的缺陷在一定程度上会牺牲器件的击穿电压、导通电阻等性能,同时影响器件的可靠性。近期,河北工业大学和广东工业大学联合开发了缺陷相关的仿真模型,深…

VMware虚拟机关机报错处理办法

VMware虚拟机关机报错处理办法 ​ 在 VMware ESXi 下面强制关闭一个沒有反应的 VM 虚拟机的方法, 一般正常都是使用 vSphere Client 去控制 VM 虚拟机的电源开关, 但是有时会发生即使用里面的 Power Off 按钮但是还是无法关闭我的 VM 虚拟机, 而且最终会出现一串 错误信息「An…

github将默认分支main改为master

github将默认分支main改为master 1.进入github,点击setting 2.在setting中,选择Respositories,更新默认分支为master 3.选择要更新的项目,在项目中选择setting->general->切换默认分支

【三勾商城】新增添加自定义表单

三勾商城是开发友好的微信小程序商城,框架支持SAAS,支持发布 iOSAndroid公众号H5各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)等多个平台,不可多得的二开神器, 为大中小企业提供极致的移动电子商务解决…

1371. 每个元音包含偶数次的最长子字符串

1371. 每个元音包含偶数次的最长子字符串 原题链接:完成情况:解题思路:参考代码:_1371每个元音包含偶数次的最长子字符串 错误经验吸取 原题链接: 1371. 每个元音包含偶数次的最长子字符串 https://leetcode.cn/pro…

Oracle和mysql中插入时间字段

例如有id 和 times两个字段 Oracle insert into xxx values|(1,sysdate) mysql insert into xxx values(1,now()) 在 MySQL 中,SYSDATE() 函数也是可用的,它与 NOW() 类似,但略有不同: NOW…

ES各种分页方式及其优缺点对比

ES各种分页方式及其优缺点对比 Elasticsearch (ES) 提供了几种不同的分页方法来帮助用户有效地从大量数据中检索部分结果。以下是几种常见的分页方式及其优缺点对比: 1. From/Size 分页(浅分页) 说明: 这是最直接也是ES默认的分页方式&…

父组件查询时调用子组件的方法

使用场景: 头部的查询条件放在父组件上,当点击查询时,下面的六个子组件会更新数据 例如: 1.在父组件上定义一个ref: 例如: 提示:在JS上定义ref,点击查询条件时调用这个ref里面的方…

“Stable Diffusion“ 是一个常见的数值分析算法,通常用于解决偏微分方程

“Stable Diffusion” 是一个常见的数值分析算法,通常用于解决偏微分方程(PDE)中的扩散问题。它的稳定性使得它在模拟自然界中的各种扩散过程时非常有用。下面是一个简单的 “Stable Diffusion” 算法的概述: 算法概述&#xff1…

手把手制作Vue3+Flask全栈项目 全栈开发之路实战篇 问卷网站(二)管理员后台

全栈开发一条龙——前端篇 第一篇:框架确定、ide设置与项目创建 第二篇:介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇:setup语法,设置响应式数据。 第四篇:数据绑定、计算属性和watch监视 第五篇 : 组件…

oracle sql 查近90天数据

要查询Oracle数据库中近90天的数据,您首先需要确定您想查询的表中有一个表示日期的字段。假设您有一个名为your_table的表,并且该表中有一个名为date_column的日期字段,那么您可以使用以下的SQL查询来获取近90天的数据: sql复制代…

MacOS - 为什么 Mac 安装软件后安装包在桌面上无法删除?

只要你将这磁盘里面的软件放到应用程序里面去了,那么用鼠标选中这个跟磁盘一样的东西,然后按下键盘上的 Command E 即可移除桌面上的这个磁盘。

404错误页面源码,简单实用的html错误页面模板

源码描述 小编精心准备一款404错误页面源码,简单实用的html错误页面模板,简单大气的页面布局,可以使用到不同的网站中,相信大家一定会喜欢的 效果预览 源码下载 https://www.qqmu.com/3375.html

Python newline用法:深入探索换行符的奥秘

Python newline用法:深入探索换行符的奥秘 在Python编程中,newline字符扮演着至关重要的角色,它不仅是文本流中的分隔符,还是格式化输出的关键元素。然而,对于许多初学者来说,newline的用法却充满了困惑和…

C#按钮事件触发

Xmal文件下 <StackPanel><Button Content"加法" Click"Button_Click_1" Width"200" Height"50" Background"Green" MouseEnter"Button_MouseEnter" MouseLeave"Button_MouseLeave"/><…

《额尔古纳河右岸》有感

看完了迟子建老师的《额尔古纳河右岸》&#xff0c;老规矩&#xff0c;写点东西吧。最近一段时间确实挺迷茫的&#xff0c;所以给自己找了点事儿&#xff0c;看看书。期初并不能认真看进去&#xff0c;慢慢的看见去之后&#xff0c;就愈发想知道故事的后来。 书里有太多关于死亡…