Rust-借用和生命周期

生命周期

一个变量的生命周期就是它从创建到销毁的整个过程。其实我们在前面已经注意到了这样的现象:

在这里插入图片描述
然而,如果一个变量永远只能有唯一一个入口可以访问的话,那就太难使用了。因此,所有权还可以借用。

借用

变量对其管理的内存拥有所有权。这个所有权不仅可以被转移(move),还可以被借用(borrow)。

借用指针的语法使用&符号或者&mut符号表示。前者表示只读借用,后者表示可读写借用。借用指针(borrow pointer)也可以称作“引用”(reference)。借用指针与普通指针的内部数据是一模一样的,唯一的区别是语义层面上的。它的作用是告诉编译器,它对指向的这块内存区域没有所有权。

在这里插入图片描述
这里会出现编译错误,信息为“cannot borrow immutable borrowed content’*v’as mutable”。原因在于Vec::push函数。它的作用是对动态数组添加元素,它的签名是

pub fn push(&mut self,value:T)

它要求self参数是一个&mut Self类型。而我们给foo传递的参数是&Vec类型,因此会报错。修复方式如下:

在这里插入图片描述
对于&mut型指针,请大家注意不要混淆它与变量绑定之间的语法。

如果mut修饰的是变量名,那么它代表这个变量可以被重新绑定;如果mut修饰的是“借用指针&”,那么它代表的是被指向的对象可以被修改。示例如下:
在这里插入图片描述

借用规则

关于借用指针,有以下几个规则:

  • 借用指针不能比它指向的变量存在的时间更长。
  • &mut型借用只能指向本身具有mut修饰的变量,对于只读变量,不可以有&mut型借用。
  • &mut型借用指针存在的时候,被借用的变量本身会处于“冻结”状态。
  • 如果只有&型借用指针,那么能同时存在多个;如果存在&mut型借用指针,那么只能存在一个;如果同时有其他的&或者&mut型借用指针存在,那么会出现编译错误。

借用指针只能临时地拥有对这个变量读或写的权限,没有义务管理这个变量的生命周期。因此,借用指针的生命周期绝对不能大于它所引用的原来变量的生命周期,否则就是悬空指针,会导致内存不安全。示例如下:

在这里插入图片描述
在这里给大家提个醒:一般情况下,函数参数使用引用传递的时候,不仅在函数声明这里要写上类型参数,在函数调用这里也要显式地使用引用运算符。

但是,有一个例外,那就是当参数为self &self &mut self等时,若使用小数点语法调用成员方法,在函数调用这里不能显式写出借用运算符。以常见的String类型来举例:

在这里插入图片描述
在这个示例中,所有的函数调用都是同样的语法,比如x.len()、x.push(‘!’)、x.into_bytes()等,但它们背后对self参数的传递类型完全不同,因此也就出现了不同的语义。

这是需要提醒大家注意的地方。当然,如果我们使用统一的完整函数调用语法,那么所有的参数传递类型在调用端都是显式写出来的。

任何借用指针的存在,都会导致原来的变量被“冻结”(Frozen)。示例如下:

在这里插入图片描述
编译结果为:

error:cannot assign to `x^because it is borrowed

因为p的存在,此时对x的改变被认为是非法的。

生命周期标记

对一个函数内部的生命周期进行分析,Rust编译器可以很好地解决。但是,当生命周期跨函数的时候,就需要一种特殊的生命周期标记符号了。

函数的生命周期标记

在这里插入图片描述
生命周期符号使用单引号开头,后面跟一个合法的名字。生命周期标记和泛型类型参数是一样的,都需要先声明后使用。在上面这段代码中,尖括号里面的’a是声明一个生命周期参数,它在后面的参数和返回值中被使用。

前面提到的借用指针类型都有一个生命周期泛型参数,它们的完整写法应该是&'a T &'a mut T,只不过在做局部变量的时候,生命周期参数是可以省略的。

生命周期之间有重要的包含关系。如果生命周期’a比’b更长或相等,则记为’a : 'b,意思是’a至少不会比’b短,英语读做“lifetime a outlives lifetime b”。对于借用指针类型来说,如果&'a是合法的,那么’b作为’a的一部分,&'b也一定是合法的。

另外,'static是一个特殊的生命周期,它代表的是这个程序从开始到结束的整个阶段,所以它比其他任何生命周期都长。这意味着,任意一个生命周期’a都满足’static : 'a。

在上面这个例子中,如果我们把变量t的真实生命周期记为’t,那么这个生命周期’t实际上是变量t从“出生”到“死亡”的区间。在函数被调用的时候,它传人的实际参数是&t,它是指向t的引用。那么可以说,在调用的时候,这个泛型参数’a被实例化为了’t。根据函数签名,基于返回类型的生命周期与参数是一致的,可以推理出test函数的返回类型是&'t i32。

如果我们把x的生命周期记为’x。这条let x =text(&t);语句实际上是把&'t i32类型的变量赋值给&'x i32类型的变量。这个赋值是否合理呢?它应该是合理的。因为这两个生命周期的关系是’t: 'x。test返回的那个指针在’t这个生命周期范围内都是合法的,在一个被’t包围的更小范围的生命周期内,它当然也是合法的。所以,上面这个例子可以编译通过。

接下来,我们把上面这个例子稍作修改,让test函数有两个生命周期参数,其中一个给函数参数使用,另外一个给返回值使用:

在这里插入图片描述
编译时果然出了问题,在&arg.member这一行,报了生命周期错误。这是为什么呢?因为这一行代码是把&'a i32类型赋值给&'b i32类型。

'a和’b有什么关系?答案是什么关系都没有。所以编译器觉得这个赋值是错误的。

怎么修复呢?指定’a:'b就可以了。'a比’b“活”得长,自然,&'a i32类型赋值给&'b i32类型是没问题的。

验证如下:

在这里插入图片描述
经过这样的改写后,我们可以认为,在test函数被调用的时候,生命周期参数’a和’b被分别实例化为了’t和’x。

它们刚好满足了where条件中的’t:'x约束。而&arg.member这条表达式的类型是&'t i32,返回值要求的是&'x i32类型,可见这也是合法的。

所以test函数的生命周期检查可以通过。

上述示例是读者比较难理解的地方。以下两种写法都是可行的:

在这里插入图片描述
这里的关键是,Rust的引用类型是支持“协变”的。

在编译器眼里,生命周期就是一个区间,生命周期参数就是一个普通的泛型参数,它可以被特化为某个具体的生命周期。

我们再看一个例子。它有两个引用参数,共享同一个生命周期标记:

在这里插入图片描述
上述示例中,select这个函数引入了一个生命周期标记,两个参数以及返回值都是用的这个生命周期标记。同时我们注意到,在调用的时候,传递的实参其实是具备不同的生命周期的。

x的生命周期明显大于y的生命周期,&x可存活的范围要大于&y可存活的范围,我们把它们的实际生命周期分别记录为’x和’y。

select函数的形式参数要求的是同样的生命周期,而实际参数是两个不同生命周期的引用,这个类型之所以可以匹配成功,就是因为生命周期的协变特性。

编译器可以把&x和&y的生命周期都缩小到某个生命周期’a以内,且满足’x:‘a,'y:‘a。返回的selected变量具备’a生命周期,也并没有超过’x和’y的范围。所以,最终的生命周期检查可以通过。

类型的生命周期标记

如果自定义类型中有成员包含生命周期参数,那么这个自定义类型也必须有生命周期参数。示例如下:

在这里插入图片描述
在使用impl的时候,也需要先声明再使用:
在这里插入图片描述
impl后面的那个’t是用于声明生命周期参数的,后面的Test<'t>是在类型中使用这个参数。

如果有必要的话,方法中还能继续引入新的泛型参数。

如果在泛型约束中有where T:'a之类的条件,其意思是,类型T的所有生命周期参数必须大于等于’a。

要特别说明的是,若是有where T:'static的约束,意思则是,类型T里面不包含任何指向短生命周期的借用指针,意思是要么完全不包含任何借用,要么可以有指向’static的借用指针。

省略生命周期标记

在某些情况下,Rust允许我们在写函数的时候省略掉显式生命周期标记。

在这种时候,编译器会通过一定的固定规则为参数和返回值指定合适的生命周期,从而省略一些显而易见的生命周期标记。比如我们可以写这样的代码:

在这里插入图片描述
实际上,它等同于下面这样的代码,只是把显式的生命周期标记省略掉了而已:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#编程-自定义属性

命名自定义属性 让我们继续漏洞修复示例,在这个示例中新的自定义属性被命名为BugFixingAttribute。通常的约定是在属性名称后添加单词Attribute。编译器通过允许您调用具有短版名称的属性来支持附加。 因此,可以如以下代码段所示编写该属性: [ BugFixing ( 122,"Sara…

C#用double.TryParse(String, Double)方法将字符串类型数字转换为数值类型

目录 一、定义 二、实例 命名空间: System 程序集: System.Runtime.dll 一、定义 将数字的字符串表示形式转换为它的等效双精度浮点数。 一个指示转换是否成功的返回值。 public static bool TryParse (string? s, out double result…

蓝桥杯备赛 | 洛谷做题打卡day3

蓝桥杯备赛 | 洛谷做题打卡day3 sort函数真的很厉害&#xff01; 文章目录 蓝桥杯备赛 | 洛谷做题打卡day3sort函数真的很厉害&#xff01;【深基9.例1】选举学生会题目描述输入格式输出格式样例 #1样例输入 #1 样例输出 #1 我的一些话 【深基9.例1】选举学生会 题目描述 学校…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-2 常用表单控件

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>常用表单控件</title> <style> form {width: 260px;margin: 0 auto;border: 1px solid #ccc;padding: 20px; } .right {float: right; } </style&g…

【学习笔记】2、逻辑代数与硬件描述语言基础

2.1 逻辑代数 &#xff08;1&#xff09;逻辑代数的基本定律和恒等式 基本定律或 “”与 “”非 “—”0-1律A0AA11AAAA A ‾ \overline{A} A1(互补律)A00A1AAAAA A ‾ \overline{A} A0 A ‾ ‾ \overline{\overline{A}} AA结合律(AB)C A(BC)(AB)CA(BC)ABC交换律AB BAABBA分…

广州市生物医药及高端医疗器械产业链大会暨联盟会员大会召开,天空卫士数据安全备受关注

12月20日&#xff0c;广州市生物医药及高端医疗器械产业链大会暨联盟会员大会在广州举办。在本次会议上&#xff0c;作为大会唯一受邀参加主题分享的技术供应商&#xff0c;天空卫士南区技术总监黄军发表《生物制药企业如何保护数据安全》的主题演讲。 做好承上启下“连心桥”…

【Spring实战】29 @Value 注解

文章目录 1. 定义2. 好处3. 示例1&#xff09;注入基本类型2&#xff09;注入集合类型3&#xff09;使用默认值4&#xff09;注入整数和其他类型 总结 在实际的应用中&#xff0c;我们经常需要从外部配置文件或其他配置源中获取参数值。Spring 框架提供了 Value 注解&#xff0…

感染了后缀为.mallox勒索病毒如何应对?数据能够恢复吗?

尊敬的读者&#xff1a; 在数字时代&#xff0c;勒索病毒如.mallox已经成为网络威胁中的重要一环。这篇文章将深入介绍.mallox勒索病毒的特征、应对策略以及如何预防这一威胁。面对复杂的勒索病毒&#xff0c;您需要数据恢复专家作为坚强后盾。我们的专业团队&#xff08;技术…

adb wifi 远程调试 安卓手机 命令

使用adb wifi 模式调试需要满足以下前提条件&#xff1a; 手机 和 PC 需要在同一局域网下。手机需要开启开发者模式&#xff0c;然后打开 USB 调试模式。 具体操作步骤如下&#xff1a; 将安卓手机通过 USB 线连接到 PC。&#xff08;连接的时候&#xff0c;会弹出请求&#x…

收银系统源码-智慧新零售系统框架

智慧新零售系统是一套线下线上打通的收银系统&#xff0c;主要给门店提供含线下收银、线上小程序商城、ERP进销存、精细化会员管理、丰富营销插件等为一体的智慧行业解决方案。智慧新零售系统有合伙人、代理商、商户、门店、收银员/导购员等角色&#xff0c;每个角色有相应的权…

Fine-tuning:个性化AI的妙术

在本篇文章中&#xff0c;我们将深入探讨Fine-tuning的概念、原理以及如何在实际项目中运用它&#xff0c;以此为初学者提供一份入门级的指南。 一、什么是大模型 ChatGPT大模型今年可谓是大火&#xff0c;在正式介绍大模型微调技术之前&#xff0c;为了方便大家理解&#xf…

C++系统笔记教程----vscode远程连接ssh

C系统笔记教程 文章目录 C系统笔记教程前言开发环境配置总结 前言 开发环境配置 Ubuntu20.24VScode 如果没有linux系统&#xff0c;但是想用其编译&#xff0c;可以使用ssh远程连接。 首先进入vscode,打开远程连接窗口&#xff08;蓝色的小箭头这&#xff09; 选择连接到主机…

Redis主从架构、哨兵集群原理实战

1.主从架构简介 背景 单机部署简单&#xff0c;但是可靠性低&#xff0c;且不能很好利用CPU多核处理能力生产环境必须要保证高可用&#xff0c;一般不可能单机部署读写分离是可用性要求不高、性能要求较高、数据规模小的情况 目标 读写分离&#xff0c;扩展主节点的读能力&…

案例:应用内字体大小调节

文章目录 介绍相关概念完整实例 代码结构解读保存默认大小获取字体大小修改字体大小 介绍 本篇Codelab将介绍如何使用基础组件Slider&#xff0c;通过拖动滑块调节应用内字体大小。要求完成以下功能&#xff1a; 实现两个页面的UX&#xff1a;主页面和字体大小调节页面。拖动…

【电子通识】开漏输出和推挽输出有什么差别?

在看一些MCU芯片手册的时候&#xff0c;能发现GPIO的功能有开漏输出和推挽式输出。那么这两种输出到底有什么差别&#xff1f; 如下是STM32F10xxx参考手册中对于GPIO的功能描述&#xff1a; 如下为GPIO内部框图&#xff1a; 在一些其他的芯片规格书中也同样看到不同的GPIO工作…

java基于Spring Boot的灾害应急救援评估调度平台

灾害应急救援平台的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品&#xff0c;体验高科技时代带给人们的方便&#xff0c;同时也能让用户体会到与以往常规产品不同的体验风格。&#xff08;1&#xff09;鉴于该系统是一款面向…

详细讲解Python连接Mysql的基本操作

目录 前言1. mysql.connector2. pymysql 前言 连接Mysql一般有几种方法&#xff0c;主要讲解mysql.connector以及pymysql的连接 后续如果用到其他库还会持续总结&#xff01; 对于数据库中的表格,本人设计如下:(为了配合下面的操作) 1. mysql.connector mysql.connector 是一…

高通平台开发系列讲解(PCIE篇)MHI (Modem Host Interface)驱动详解

文章目录 一、MHI驱动代码二、MHI读数据流程三、MHI写数据流程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢MHI (Modem Host Interface)我们通过名字顾名思义知道,它是Modem与Host的桥梁。 MHI 可以很容易地适应任何外围总线,但它主要用于基于 PCIe 的设备。 MHI(…

MathType中文网站2024最新版本下载及嵌入word教程

MathType是一款专业的数学公式编辑器,兼容Office word,excel等700多种程序,用于编辑数学试卷、书籍、报刊、论文、幻灯演示等文档轻松输入各种复杂的数学公式和符号。 MathType是一款功能强大的数学公式编辑器&#xff0c;广泛用于编写和编辑数学公式。Word是微软公司推出的文…

力扣精选算法100题——水果成篮(滑动窗口专题)

本题链接&#x1f449;水果成篮 第一步&#xff1a;了解题意 我就按照实例1来进行对这题的理解。 1代表种类类型&#xff0c;这个数组里面有2个种类类型 ps:种类1和种类2 &#xff0c;只不过种类1是有2个水果&#xff0c;种类2有一个水果&#xff0c;共计3个水果。 本题需要解…