【第五课】Rust所有权系统(一)

目录

前言

所有权机制的核心

再谈变量绑定

主人变更-所有权转移

总结


前言

这节课我们来介绍下rust中最重要的一个点:所有权系统。这是网上经常说rust无gc的秘密所在。在开始之前,我们来想想JVM系语言,在做垃圾回收的过程,1.怎么找到垃圾,或者说怎么判断该内存上放的是垃圾。2.什么时候清理垃圾。3.怎么清除垃圾。JVM上不同的垃圾回收器,不同的垃圾回收算法都是围绕着这三个问题展开。我们假设一个场景:中学班级毕业,所有毕业的同学需要写完毕业手册后才能离开教室,离开教室后可以清理该同学的东西了。怎么做呢?当同学离开教室的时候,这个同学的东西就可以被清理了。什么叫做离开教室呢?我们都知道代码是有作用域的,说的简单就是那对{},当某个变量离开作用域后,这个变量如果是某块内存的所有者,那么这块内存就可以被释放了,是不是很有趣的思路,其实在c/c++中,内存的释放是程序员自己写的,rust中的内存释放是靠所有权系统保证的,在编译阶段rust编译器会根据所有权系统的规则对代码进行检查,保证内存都能正确的申请和释放,如果不满足规则,编译就不允许通过,这也是很多人说rust不能一次编译,但是一旦编译通过,就是安全可靠的程序。这节课我们来看看rust中所有权系统吧,提前预告涉及的概念多且陌生,耐心看完。

听起来rust中的内存回收是不是挺简单的,只要同学离开教室就可以清理该同学的东西了,假设某本字典是A同学和B同学一起买的,当A同学离开的时候,把字典清理了,这时候B同学想查字典就会造成空指针,这就是悬垂引用,当B同学离开教室时,想要再扔一次字典,此时字典已经被扔过一次,这就会造成内存二次释放。这2个问题都是比较严重的问题。

涉及概念

所有权系统ownership system

所有权ownership

借用borrowing

生命周期lifetimes

所有权机制的核心

1.每一块内存空间都有一个所有者,该所有者对于该内存有读写权限和释放内存的权限,我们将这个所有者称为主人

2.每一块内存空间在任意时刻都只有一个主人,不能同时存在2个主人,比如上面例子中的字典,字典只能有一个主人,但是A同学可以把字典给同学B,这样主人就从A转移给B,其他同学可以借字典使用,但是B才是主人,哪些不是主人的同学没有释放内存的权限,所以离开教室时不会丢字典

3.当主人离开作用域时,该主人释放他的内存区域

再谈变量绑定

再一次看看变量绑定

fn main() {let x = "hello".to_string();
}

知道为什么在其他语言中这样的代码叫做变量x的声明和初始化,但是在rust中这叫做变量绑定了吧,将字符串绑定到变量x上,从此之后,x就是hello所在内存的主人了,当x走出了作用域之后,hello所在的内存就要被清理。

主人变更-所有权转移

我们说过主人是可以变更的,那么怎么变更呢,通过赋值,下面的代码hello的主人第一次是x,在y = x后,hello的主人已经是y了,这种行为叫做移动move,也叫做所有权转移,但是这里要注意一下,有些值是移动,有些值是复制,比如一些基本类型,例如数字,当操作y=x时,是复制了一个值出来,假设x=7,当操作y=x时,此时内存中有2个7,他们的主人分别是x和y,但是像字符串这样的,才是移动。

重要的3句话:

1.rust中有2种语义:复制语义和移动语义

2.复制语义不会导致所有权转移,移动语义会导致所有权变更

3.实现了Copy trait的都是复制语义,没有实现Copy trait的都是移动语义

所以很简单了,根据上面的三句话,基本类型实现了Copy,所有权不会转移,String没有实现Copy,所以会转移,对于元组,如果元组中的所有值都实现了Copy,那么元组也实现了Copy,不会转移。我贴了一个例子在后面。

fn main() {let x = "hello".to_string();{let y = x;}
}

看看这个元组的所有权例子,t1的所有元组都实现了Copy,所以t1也实现了Copy,于是在t2 = t1时,会将这个元组复制一份,t1 和 t2分别是2块不同内存的主人,因为是主人,所以我们可以访问t1的元素。t3由于存在一个String类型的元素,String是移动语义,所以t3也是移动语义,那么在t4=t3时,该元组的内存已经属于了t4,不再是t3的了,所有权发生了转移,那么当我们继续使用t3.1时,会收到报错,下面贴了报错的图,可以看到rust编译器解释的很清楚,t3因为没有实现Copy,所以它被移动了,移动了之后,我们就不能使用t3去读取元组了,因为t3不是主人了,不是主人就没有读写权限了。

fn main() {// 元组的所有元素都实现了copy,元组就实现了copylet t1 = (1, 2, true);let t2 = t1; // 所有权不会转移println!("t1.0 = {}", t1.0);// 元组存在元素没有实现copy,元组没有实现copylet t3 = (String::from("hello"), 1, 2, 3);let t4 = t3; // 这里所有权转移给t4, t3不再是主人println!("t3.1 = {}", t3.1);
}

那么结构体呢?结构体会发生所有权转移么?我们来试一试,实践出真知。

代码如下,其实对于结构体来说,不管结构体中的元素有没有实现copy,结构体默认都是移动语义,所有权会发生移动。但是大家会问,这不合理啊,如果结构体中的元素都是copy语义,结构体其实也可以是copy语义啊,是的,没错。所以说结构体默认是移动语义,如果结构体的元素都是copy语义,可以在结构体顶部加上#[derive(Copy,Clone)]后,结构体就是copy语义了,但是如果结构体中存在移动语义的元素,即使加了这个,结构依然是移动语义,关于这个derive属性,可以百度了解一下哦。

fn main() {let s1 = Student1{age: 23,};let s2 = s1;println!("s1.age = {}", s1.age);let s3 = Student2 {name: String::from("hello"),};let s4 = s3;println!("s3.name = {}", s3.name);}struct Student1 {age: i32,
}struct Student2 {name: String,
}

总结

这节课是rust无gc的核心秘密所在,也是和其他编程语言非常不同的地方,rust的所有权保证了每一块内存都有自己唯一的主人,当主人离开作用域后,内存可以释放,从而rust的性能可以堪比c/c++这些需要手动申请释放内存的语言,rust程序需要的内存量比起java来说也少了很多倍,原因就在这里了。这一节课值得好好研究,也可以在网上看些其他材料综合理解,同时也欢迎大家评论交流哈。

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

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

相关文章

三周精通FastAPI:42 手动运行服务器 - Uvicorn Gunicorn with Uvicorn

官方文档:Server Workers - Gunicorn with Uvicorn - FastAPI 使用 fastapi 运行命令 可以直接使用fastapi run命令来启动FastAPI应用: fastapi run main.py如创建openapi.py文件: from fastapi import FastAPIapp FastAPI(openapi_url&…

任意文件下载漏洞

1.漏洞简介 任意文件下载漏洞是指攻击者能够通过操控请求参数,下载服务器上未经授权的文件。 攻击者可以利用该漏洞访问敏感文件,如配置文件、日志文件等,甚至可以下载包含恶意代码的文件。 这里再导入一个基础: 你要在网站下…

编写一个生成凯撒密码的程序

plain list(input("请输入需要加密的明文(只支持英文字母):"))key int(input("请输入移动的位数:"))base_A ord(A)base_a ord(a)cipher []for each in plain:if each :cipher.append( )else:if each.i…

RDIFramework.NET CS敏捷开发框架 V6.1发布(.NET6+、Framework双引擎、全网唯一)

RDIFramework.NET C/S敏捷开发框架V6.1版本迎来重大更新与调整,全面重新设计业务逻辑代码,代码量减少一半以上,开发更加高效。全系统引入全新字体图标,整个界面焕然一新。底层引入最易上手的ORM框架SqlSugar,让开发更加…

华为USG5500防火墙配置NAT

实验要求: 1.按照拓扑图部署网络环境,使用USG5500防火墙,将防火墙接口加入相应的区域,添加区域访问规则使内网trust区域可以访问DMZ区域的web服务器和untrust区域的web服务器。 2.在防火墙上配置easy-ip,使trust区域…

Java基础-I/O流

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 字节流 定义 说明 InputStream与OutputStream示意图 说明 InputStream的常用方法 说明 OutputStrea…

RabbitMQ的工作队列在Spring Boot中实现(详解常⽤的⼯作模式)

上文着重介绍RabbitMQ 七种工作模式介绍RabbitMQ 七种工作模式介绍_rabbitmq 工作模式-CSDN博客 本篇讲解如何在Spring环境下进⾏RabbitMQ的开发.(只演⽰部分常⽤的⼯作模式) 目录 引⼊依赖 一.工作队列模式 二.Publish/Subscribe(发布订阅模式) …

<项目代码>YOLOv8 番茄识别<目标检测>

YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…

Python数据分析NumPy和pandas(三十五、时间序列数据基础)

时间序列数据是许多不同领域的结构化数据的重要形式,例如金融、经济、生态学、神经科学和物理学。在许多时间点重复记录的任何内容都会形成一个时间序列。许多时间序列是固定频率的,也就是说,数据点根据某些规则定期出现,例如每 1…

【C++滑动窗口】1248. 统计「优美子数组」|1623

本文涉及的基础知识点 C算法:滑动窗口及双指针总结 LeetCode1248. 统计「优美子数组」 给你一个整数数组 nums 和一个整数 k。如果某个连续子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。 请返回这个数组中 「优美子数组」 的数…

【不写for循环】玩玩行列

利用numpy的并行操作可以比纯用Python的list快很多,不仅如此,代码往往精简得多。 So, 这篇来讲讲进阶的广播和花哨索引操作,少写几个for循环()。 目录 一个二维的例题 一个三维的例题 解法一 解法二 更难的三维例题…

《Java核心技术 卷I》用户界面中首选项API

首选项API 在桌面程序中,通常都会存储用户首选项,如用户最后处理的文件、窗口的最后位置等。 利用Properties类可以很容易的加载和保存程序的配置信息,但有以下缺点: 有些操作系统没有主目录概念,很难为匹配文件找到…

3步实现贪吃蛇

方法很简单,打开页面,复制,粘贴 一.整体思维架构 我们根据游戏的开始,运行,结束,将整个游戏划分成三个部分。在每个部分下面又划分出多个功能,接下来我们就根据模块一一实现功能。 二.Gamesta…

STL序列式容器之list

相较于vector的连续性空间&#xff0c;list相对比较复杂&#xff1b;list内部使用了双向环形链表的方式对数据进行存储&#xff1b;list在增加元素时&#xff0c;采用了精准的方式分配一片空间对数据及附加指针等信息进行存储&#xff1b; list节点定义如下 template<clas…

【论文模型复现】深度学习、地质流体识别、交叉学科融合?什么情况,让我们来看看

文献&#xff1a;蓝茜茜,张逸伦,康志宏.基于深度学习的复杂储层流体性质测井识别——以车排子油田某井区为例[J].科学技术与工程,2020,20(29):11923-11930. 本文目录 一、前言二、文献阅读-基于深度学习的复杂储层流体性质测井识别2.1 摘要2.2 当前研究不足2.3 本文创新2.4 论文…

(一)- DRM架构

一&#xff0c;DRM简介 linux内核中包含两类图形显示设备驱动框架&#xff1a; FB设备&#xff1a;Framebuffer图形显示框架; DRM&#xff1a;直接渲染管理器&#xff08;Direct Rendering Manager&#xff09;&#xff0c;是linux目前主流的图形显示框架&#xff1b; 1&am…

Java基础-Java中的常用类(上)

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 String类 创建字符串 字符串长度 连接字符串 创建格式化字符串 String 方法 System类 常用方法 方…

Istio分布式链路监控搭建:Jaeger与Zipkin

分布式追踪定义 分布式追踪是一种用来跟踪分布式系统中请求的方法&#xff0c;它可以帮助用户更好地理解、控制和优化分布式系统。分布式追踪中用到了两个概念&#xff1a;TraceID 和 SpanID。 TraceID 是一个全局唯一的 ID&#xff0c;用来标识一个请求的追踪信息。一个请求…

探索Python网络请求新纪元:httpx库的崛起

文章目录 **探索Python网络请求新纪元&#xff1a;httpx库的崛起**第一部分&#xff1a;背景介绍第二部分&#xff1a;httpx库是什么&#xff1f;第三部分&#xff1a;如何安装httpx库&#xff1f;第四部分&#xff1a;简单的库函数使用方法1. 发送GET请求2. 发送POST请求3. 超…

vue使用List.reduce实现统计

需要对集合的某些元素的值进行计算时&#xff0c;可以在计算属性中使用forEach方法 1.语法&#xff1a;集合.reduce ( ( 定义阶段性累加后的结果 , 定义遍历的每一项 ) > 定义每一项求和逻辑执行后的返回结果 , 定义起始值 ) 2、简单使用场景&#xff1a;例如下面…