【Rust自学】10.5. 生命周期 Pt.1:生命周期的定义与意义、借用检查器与泛型生命周期

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

10.5.1. 什么是生命周期

Rust的每个引用都有自己的生命周期,生命周期的作用是让引用保持有效,也可以说它是保持引用有效的作用域。

在大多数情况下,生命周期是隐式的、可推断的。如果引用的生命周期可能以不同的方式相互关联时,就必须手动地标注生命周期。

生命周期可以说是Rust与其它语言相比最与众不同的特征,所以它非常难学。

10.5.2. 生命周期的存在意义

生命周期存在的主要目的是为了避免悬空引用(Dangling reference,又叫悬垂引用),这个概念在4.4. 引用与借用 中有讲过,我把当时对悬空引用的解释粘到这来:

在使用指针时非常容易引起叫做悬空指针(Dangling Pointer) 的错误,其定义为:一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其他人使用了如果你引用了某些数据,Rust编译器保证在引用离开作用域前数据不会离开作用域。 这是Rust保证悬空引用永远不会出现的做法。

看个例子:

fn main() {
	let r;
	{ //小花括号
		let x = 5;
		r = &x;
	}
	println!("{}", r);
}
  • 在这个例子中先声明了r,但是没有初始化,这么做的目的是让r存在于小花括号外(看代码的注释的位置)的作用域。当然,Rust没有Null值,所以在r初始化前不能使用r
  • 而在小花括号内声明了变量x,赋值为5。下边一行把x的引用赋给了r
  • 小花括号这个作用域结束之后,在它外面又打印了r

这段代码是有错误的,错误在于当打印r时,x已经走出作用域被销毁了。所以r的值,也就是x的引用此时指向的内存地址是已经被释放的内存,指向的数据已经不是x了,造成了悬空引用,所以会报错。

输出:

error[E0597]: `x` does not live long enough
 --> src/main.rs:5:7
  |
4 |         let x = 5;
  |             - binding `x` declared here
5 |         r = &x;
  |             ^^ borrowed value does not live long enough
6 |     }
  |     - `x` dropped here while still borrowed
7 |     println!("{}", r);
  |                    - borrow later used here

报错信息是借用的值活的时间不够长。因为在内部花括号结束的时候x走出作用域,但r作用域更大能够继续使用,为了保证程序的安全性,这个时候任何基于r的操作都是无法正确运行的。

Rust会通过借用检查器来检查代码的合法性。

10.5.3. 借用检查器(borrow tracker)

借用检查器会比较作用域来判断所有的借用是否合法。对于刚才那个代码例,借用检查器发现r的值是x的引用,但是r的生命周期比x长,就会报错。

怎么解决这个问题呢?很简单,让x的生命周期不小于r就行:

fn main() {
	let x = 5;
	let r = &x;
	println!("{}", r);
}

这个时候x的生命周期是从第2行到第5行,r的生命周期是从第3行到第5行,所以x的生命周期就完全覆盖了r的生命周期,程序就不会报错。

10.5.4. 函数中的泛型生命周期

看个例子:

fn main() {  
    let string1 = String::from("abcd");  
    let string2 = "xyz";  
  
    let result = longest(string1.as_str(), string2);  
    println!("The longest string is {result}");  
}  
  
fn longest(x: &str, y: &str) -> &str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}
  • string1这个变量是String类型,而string2的类型是字符串切片&str,然后把这两个值传进longest函数(string1需要先转化一下成&str类型),把得到的返回值打印出来。

  • longest函数的逻辑是把输入的两个参数做对比,选更长的那个返回

输出:

error[E0106]: missing lifetime specifier
 --> src/main.rs:9:33
  |
9 | fn longest(x: &str, y: &str) -> &str {
  |               ----     ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
  |
9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  |           ++++     ++          ++          ++

错误是缺少生命周期的标注,具体地说是返回类型缺少生命周期参数。看下面的help,说这个函数的返回类型包含了一个借用的值,但是函数的签名没有说明这个借用的值是来自x还是来自y,考虑引入一个命名的生命周期参数。

看回这个函数:

fn longest(x: &str, y: &str) -> &str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

很明显,这个函数的返回值要么是x要么是y,但具体是哪个不一定,而xy这两个传入的参数的具体生命周期也是不知道的(只看这个函数的情况下)。所以没法像之前的例子那样比较作用域,从而判断返回的引用是否是一直有效的。借用检查器也做不到,原因就是它不知道这个返回类型的生命周期到底是跟x有关还是跟y有关。

实际上就算返回值是确定的这么写也会报错:

fn longest(x: &str, y: &str) -> &str {  
    x
}

输出:

error[E0106]: missing lifetime specifier
 --> src/main.rs:9:33
  |
9 | fn longest(x: &str, y: &str) -> &str {
  |               ----     ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
  |
9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  |           ++++     ++          ++          ++

编译器还是不清楚,因为函数类型体现不出来返回类型借用的值是来自x还是来自y

所以这个事跟函数体里的逻辑没有关系,就是跟函数签名有关系,那该怎么改呢?我们其实可以按照报错信息里的帮助提示来改:

= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
  |
9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  |           ++++     ++          ++          ++

它让我们加个泛型生命周期参数我们就加:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

'a表示有a这么一个生命周期,xy以及返回类型都是这个生命周期a,这个时候就表示xy和返回类型的生命周期是一样的。

“一样的”这个说法不太准确,因为xymain函数对应的实例的生命周期其实有一点差别,但这个内容下篇文章再讲。

先看看代码整体:

fn main() {  
    let string1 = String::from("abcd");  
    let string2 = "xyz";  
  
    let result = longest(string1.as_str(), string2);  
    println!("The longest string is {result}");  
}  
  
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

输出:

The longest string is abcd

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

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

相关文章

Vue2: table加载树形数据的踩坑记录

table中需要加载树形数据,如图: 官网给了两个例子,且每个例子中的tree-props都是这么写的: :tree-props="{children: children, hasChildren: hasChildren}" 给我一种错觉,以为数据结构中要同时指定children和hasChildren字段,然而,在非懒加载模式下,数据结…

深入了解 SSL/TLS 协议及其工作原理

深入了解 SSL/TLS 协议及其工作原理 一. 什么是 SSL/TLS?二. SSL/TLS 握手过程三. SSL/TLS 数据加密与传输四. 总结 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱 一. 什么是 SSL/TLS? 安全套接层&am…

sqlserver sql转HTMM邮件发送

通过sql的形式&#xff0c;把表内数据通过邮件的形式发送出去 declare title varchar(100) DECLARE stat_date CHAR(10),create_time datetime SET stat_dateCONVERT(char(10),GETDATE(),120) SET create_timeDATEADD(MINUTE,-20,GETDATE()) DECLARE xml NVARCHAR (max) DECLAR…

用QT实现 端口扫描工具1

安装在线QT&#xff0c;尽量是完整地自己进行安装&#xff0c;不然会少包 参考【保姆级图文教程】QT下载、安装、入门、配置VS Qt环境-CSDN博客 临时存储空间不够。 Windows系统通常会使用C盘来存储临时文件。 修改临时文件存储位置 打开系统属性&#xff1a; 右键点击“此电…

Selenium 自动化,如何下载正确的 ChromeDriver

在 Python 的 Selenium 自动化操作中&#xff0c;chromedriver 是不可或缺的驱动程序。没有正确安装对应版本的驱动&#xff0c;运行代码时常常会遇到报错问题&#xff0c;比如 “session not created: This version of ChromeDriver only supports Chrome version XX”。 今天…

泊松融合 实例2025

目录 例子1: 实现代码: 原作者代码: 本博客直接给出来最好的效果和源代码 参数说明: 效果不好,不推荐的参数:MONOCHROME_TRANSFER,NORMAL_CLONE 例子1: 目标图: 原图: 效果图: 实现代码: 坐标是要目标图上中心点坐标: import cv2if __na

前端如何从入门进阶到高级

在前端学习的道路上&#xff0c;我们将其划分为三个阶段&#xff1a;入门、实战和进阶。以下是各阶段的学习指南 一、入门阶段 在入门阶段&#xff0c;我们的目标是掌握前端的基本语法和知识&#xff0c;以便能够独立解决一些基础问题。这一阶段&#xff0c;我们建议通过视频…

Python爬虫基础——认识网页结构(各种标签的使用)

1、添加<div>标签的代码定义了两个区块的宽度和高度均为100px&#xff0c;边框的格式也相同&#xff0c;只是区块中显示的内容不同&#xff1b; 2、添加<ul>和<ol>标签分别用于定义无序列表和有序列表。<il>标签位于<ul>标签或<ol>标签之…

基于W2605C语音识别合成芯片的智能语音交互闹钟方案-AI对话享受智能生活

随着科技的飞速发展&#xff0c;智能家居产品正逐步渗透到我们的日常生活中&#xff0c;其中智能闹钟作为时间管理的得力助手&#xff0c;也在不断进化。基于W2605C语音识别与语音合成芯片的智能语音交互闹钟&#xff0c;凭借其强大的联网能力、自动校时功能、实时天气获取、以…

Python提取目标Json键值:包含子嵌套列表和字典

目标&#xff1a;取json中所有的Name、Age字典 思路&#xff1a;递归处理字典中直接包含子字典的情况&#xff0c; import jsondef find_targ_dicts(data,key1,key2):result {}if isinstance(data, dict):if key1 in data and key2 in data: # 第一层字典中包含key1和key2re…

你已经分清JAVA中JVM、JDK与JRE的作用和关系了吗?

你已经分清JAVA中JVM、JDK与JRE的作用和关系了吗&#xff1f; 一. JVM、JDK与JRE的关系二. JVM、JDK与JRE的作用2.1 什么是JVM&#xff1f;2.2 什么是JDK&#xff1f;2.3 什么是JRE&#xff1f; 前言 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有…

深度学习blog-RAG构建高效生成式AI的优选路径

RAG&#xff08;Retrieval-Augmented Generation&#xff09; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;模型的性能和应用场景也不断扩展。其中&#xff0c;检索增强生成&#xff08;RAG, Retrieval-Augmented Generation&#xff09;模型作为一种新…

数据中台与数据治理服务方案[50页PPT]

本文概述了数据中台与数据治理服务方案的核心要点。数据中台作为政务服务数据化的核心&#xff0c;通过整合各部门业务系统数据&#xff0c;进行建模与加工&#xff0c;以新数据驱动政府管理效率提升与政务服务能力增强。数据治理则聚焦于解决整体架构问题&#xff0c;确保数据…

AI生成PPT,效率与创意的双重升级

AI生成PPT&#xff0c;效率与创意的双重升级&#xff01;在信息化高速发展的今天&#xff0c;我们的工作节奏被无限压缩&#xff0c;效率成为了衡量工作能力的重要指标。而制作PPT这种事&#xff0c;总是让人又爱又恨——既想做得出彩&#xff0c;又不想花费大量时间。现在有了…

【HF设计模式】05-单例模式

声明&#xff1a;仅为个人学习总结&#xff0c;还请批判性查看&#xff0c;如有不同观点&#xff0c;欢迎交流。 摘要 《Head First设计模式》第5章笔记&#xff1a;结合示例应用和代码&#xff0c;介绍单例模式&#xff0c;包括遇到的问题、采用的解决方案、以及达到的效果。…

嵌入式linux系统中QT信号与槽实现

第一:Qt中信号与槽简介 信号与槽是Qt编程的基础。因为有了信号与槽的编程机制,在Qt中处理界面各个组件的交互操作时变得更加直观和简单。 槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。 案例操作与实现: #ifndef …

php有两个数组map比较 通过id关联,number可能数量变化 比较他们之间增加修改删除

在PHP中&#xff0c;比较两个通过ID关联的数组&#xff0c;并确定它们之间的增加、修改和删除操作&#xff0c;你可以使用以下步骤&#xff1a; 创建两个数组&#xff1a;假设你有两个数组&#xff0c;分别表示“旧数据”和“新数据”。使用ID作为键&#xff1a;为了方便比较&a…

C++和OpenGL实现3D游戏编程【连载19】——着色器光照初步(平行光和光照贴图)(附源码)

1、本节要实现的内容 我们在前期的教程中,讨论了在即时渲染模式下的光照内容。但在我们后期使用着色器的核心模式下,会经常在着色器中使光照,我们这里就讨论一下着色器光照效果,以及光照贴图效果,同时这里知识会为后期的更多光照效果做一些铺垫。本节我们首先讨论冯氏光照…

《learn_the_architecture_-_generic_interrupt_controller_v3_and_v4__overview》学习笔记

1.GIC是基于Arm GIC架构实现的&#xff0c;该架构已经从GICv1发展到最新版本GICv3和GICv4。 Arm 拥有多个通用中断控制器&#xff0c;可为所有类型的 Arm Cortex 多处理器系统提供一系列中断管理解决方案。这些控制器的范围从用于具有小型 CPU 内核数的系统的最简单的 GIC-400 …

健身房管理系统多身份

本文结尾处获取源码。 本文结尾处获取源码。 本文结尾处获取源码。 一、相关技术 后端&#xff1a;Java、JavaWeb / Springboot。前端&#xff1a;Vue、HTML / CSS / Javascript 等。数据库&#xff1a;MySQL 二、相关软件&#xff08;列出的软件其一均可运行&#xff09; I…