当前位置: 首页 > news >正文

rust踩雷笔记3——生命周期的理解

目录

      • 概念和基本使用
        • 一个例子彻底理解最基本的内容
      • 一个例子理解函数签名为什么要有生命周期标注
      • ⭐️能不能对编译器蒙混过关?

生命周期是rust中最难的概念——鲁迅

这一块内容即便是看rust圣经,第一遍也有点懵。今天早上二刷突然有了更直观的认识,记录一下。

概念和基本使用

生命周期就是应用的有效作用域,它的主要作用是避免悬垂引用。

悬垂引用的典型例子:

{let y;{let x = 1;y = &x;}println!("y: {}", y);
}

简而言之就是y引用的变量在y被使用之前就释放了。

我们通过肉眼检查上面代码,会发现x的生命周期没有延续到y被使用的时候,所以会发现问题。rust为了安全做出了很多约束,所以这里即便是我们不用肉眼观察,也能发现问题。

一个例子彻底理解最基本的内容

让我们直接切入使用,来体会生命周期的用法。

首先,就算你什么都不做,编译器大多数可以自动推导出生命周期。

如果你要手动标注声明周期,那么就加'a符号,'是重点,a的话可以替换。

看如下代码:

let y;
{let x = 1;y = &x;
}
println!("y: {}", y);

很显然发生了悬垂引用,报错信息:
x does not live long enough, borrowed value does not live long enough
x的生命周期在花括号内,想要避免悬垂引用,需要将x的生命周期延长到y的位置

方法一✔️:

let x = 1;
let y;
{y = &x;
}
println!("y: {}", y);

这样肯定就没错了,x作为被引用的变量,生命周期比引用它的y长

方法二❎:
不改变代码,只是添加生命周期引用?

当然不行!

生命周期引用只是为了向编译器做出说明,如果加了'a的话,就说明引用的作用域大于等于’a。如果对多个引用加同一个’a,说明这些引用的作用域都大于等于’a。可以看到,这是一种约束条件;
生命周期标注’a并不改变引用的真实生命周期,只是告诉编译器,当不满足’a表示的约束条件时,就报错。

⭐️一个例子体会生命周期的作用——让编译器检查约束条件

let x = 5;
{let y = &x;
}

这个代码肯定没有问题,被引用的x比引用y活得久。

那如果此时我加入生命周期约束

let x = 5;
{let y: &'static i32 = &x;
}

这是在告诉编译器y具有全局生命周期,但实际上y的生命周期在花括号内,上述代码没有悬垂引用。

那你觉得此时,编译器报不报错?
当然会!记住两个原则:

  1. 生命周期标注不改变引用的真实生命周期(比如上述的y还是在花括号内)
  2. 编译器会相信你标注的生命周期,而不是引用真实的生命周期

没错,当你标注’static起,编译器就认为y可以活全局,那么此时它检查x和y的约束关系,就会按照这个来。所以报错信息是:
x does not live long enough, borrowed value does not live long enough
编译器觉得x不能活全局,所以y活全局的话,会发生悬垂引用。

⚠️注意:
问:上述代码有没有发生悬垂引用?
答:没有。
问:'static有没有改变y的真实生命周期?
答:没有。
问:那为啥报了悬垂引用的错误?
答:因为编译器相信你的标注,从而它认为y就是活全局的。

一个例子理解函数签名为什么要有生命周期标注

摘自rust圣经的一段代码:

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}
}

这个程序会报错:
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from x or y

我们来分析一下,当longest被调用的时候,x引用string1,y引用string2(严格讲string2本身是&str)。result因为获取返回值,所以是引用string1或者string2,那么问题来了,rust因为严格的安全检查,此时要检查是否有悬垂引用,即检查是否有被引用的变量生命周期大于引用变量。

可是编译期间是无法获知result会引用哪个字符串的,编译器这个大聪明就无法检查是否有悬垂引用。

rust圣经说了,改成这个就不会有问题了:

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

记住分析:编译器会认为x和y生命周期都不小于’a,并且’a的大小就是x和y中生命周期的交集。所以相当于告诉了编译器,把具体引用传给返回值的时候,返回值的生命周期为x和y生命周期的交集。

如下例子深入理解:
(依旧摘自rust圣经)

fn main() {let string1 = String::from("long string is long");let result;{let string2 = String::from("xyz");result = longest(string1.as_str(), string2.as_str());}println!("The longest string is {}", result);
}

分析:我们告诉编译器的结论是,'a的生命周期是string1和string2的交集,也就是string2的生命周期;因此result生命周期等于string2的生命周期。

然后,编译器根据我们的结论,去检查代码有没有悬垂引用的风险,虽然上述函数调用,会使得result引用的是string1,这样一看没有悬垂引用。但是编译期间不知道result会引用谁,此时编译器一看,如果引用的是string2,那么悬垂引用就会发生,所以就会报错。

由于此例子中result只会引用string1,所以代码也许可以这样改:

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

就不会出错了。

先写这么多,剩下的结合实践深入了解。

我又回来了,因为发现一个更好做说明的例子:

⭐️能不能对编译器蒙混过关?

这个标题什么意思呢,我们再把rust圣经中一段代码摘过来

fn main() {let string1 = String::from("long string is long");let result;{let string2 = String::from("xyz");result = longest(string1.as_str(), string2.as_str());}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}
}

刚刚说了,这个生命周期标注会让编译器意识到有悬垂引用风险,于是拒绝代码执行

safety这个单词被发明出来前,还有个单词表示安全,叫rusty——鲁迅

这时候咱可能不爽了,你看明明string1更长,longest只会返回对string1的引用,肉眼就能发现没有悬垂引用,为什么编译器这个大聪明一定要去检查string2呢?

不行,不能惯着编译器,我们修改代码:

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

我直接把y的’a标注删掉,看你怎么检查string2.

结果编译器也是倔强,反手来了个新错误:
explicit lifetime required in the type of y, lifetime 'a required
也就是说没有标注编译器就没办法检查悬垂引用,没办法检查就会直接报错。

那我继续改:

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

改成’b总可以了吧,还是不行:
lifetime may not live long enough, consider adding the following bound: 'b: 'a
'b: 'a表示’b生命周期大于’a。

那继续改:

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

结果依旧是报错:
string2 does not live long enough

你会发现,

  • 不标注y生命周期的时候,编译器因为无法检查悬垂引用而报错;
  • 给y标注一个’b后,因为函数返回值是’a,编译器无法判断’b和’a的关系,也就无法检查悬垂引用;
  • 标注’a: 'b或者’b: 'a都可以表示两者关系,但是我们只能选没有悬垂引用的那种;
  • longest返回值是一个引用,它可能会引用y,所以这里只能是’b: 'a,如果你标注’a: 'b,说明你告诉编译器返回值获得比y久,那么编译器会相信你说的,就直接因为可能发生悬垂引用报错;
  • 那你正好标注’b: 'a,但是编译器发现和事实不符,于是报错。

⚠️总结:

  1. 你给编译器说什么,编译器就信什么,所以你不能乱说,要标注正确的生命周期,以及不同生命周期的关系。如果编译器从你的标注就感觉到了悬垂引用,就会报错;
  2. 你标注了什么,不代表真实的生命周期就是什么,你的标注只是告诉编译器你希望的事实,如果实际情况不满足标注的约束,编译器就会报错。

简版:
你的标注若有悬垂引用,哪怕实际上没有,编译器也会报错;
你的标注若与事实不符,标注没有悬垂引用而实际有或者有风险发生,编译器报错。

所以是否报错要看:
标注是否正确+事实是否符合正确的标注约束

相关文章:

rust踩雷笔记3——生命周期的理解

目录 概念和基本使用一个例子彻底理解最基本的内容 一个例子理解函数签名为什么要有生命周期标注⭐️能不能对编译器蒙混过关&#xff1f; 生命周期是rust中最难的概念——鲁迅 这一块内容即便是看rust圣经&#xff0c;第一遍也有点懵。今天早上二刷突然有了更直观的认识&…...

windows权限维持—黄金白银票据隐藏用户远控RustDeskGotoHttp

windows权限维持—黄金白银票据&隐藏用户&远控&RustDesk&GotoHttp 1. 前置1.1. 初始问题1.1.1. 解决办法 2. 隐藏用户2.1. 工具原理2.2. 案例操作2.2.1. 单机添加用户2.2.1.1. 工具添加用户2.2.1.2. 工具查看隐藏用户2.2.1.3. 本地查看隐藏用户 2.2.2. 域内添加…...

vscode conda activate激活环境出错

vscode conda activate 出错 conda-script.py: error: argument COMMAND: invalid choice: ‘activate’ To initialize your shell, run$ conda init <SHELL_NAME>Currently supported shells are:- bash- fish- tcsh- xonsh- zsh- powershellSee conda init --help f…...

信息与通信工程面试准备——数学知识|正态分布|中心极限定理

目录 正态分布 正态分布的参数 正态分布的第一个参数是均值 正态分布的第二个参数是标准差SD 所有正态分布的共同特征 标准正态分布&#xff1a;正态分布的特例 中心极限定理 理解定义 示例# 1 示例# 2 知道样本均值总是正态分布的实际含义是什么&#xff1f; 正态分…...

Mybatis多表查询与动态SQL的使用

目录 1. Mybatis多表查询 1.1 添加文章表实体类 1.2 文章Interface 1.3 文章.xml 1.4 lombok的toString()有关对象打印的说明 1.5 场景: 一个用户查询多篇文章 2. 复杂情况: 动态SQL的使用 2.1 为什么要使用动态SQL? 2.2 <if>标签 2.3 <trim>标签 2.4 <where&g…...

url 和 uri 有什么区别?

URL&#xff08;Uniform Resource Locator&#xff09;和URI&#xff08;Uniform Resource Identifier&#xff09;是两个与网络资源定位和标识相关的概念&#xff0c;它们有一些区别&#xff0c;但也存在一些重叠。 URI&#xff08;Uniform Resource Identifier&#xff09;是…...

HCIP VLAN实验

VLAN实验 拓扑图配置和分析分析配置LSW1LSW2R1 测试dhcp获取ipICMP测试 拓扑图 配置和分析 分析 从题目来看&#xff0c;因为 pc 1 3都是vlan2而且还是不同网段&#xff0c;pc 2 4 5 6在同一网段&#xff0c;所以可以将pc 1 2 5 4 6分在一个网段 pc4不通5 6 &#xff0c;那就…...

无涯教程-Perl - waitpid函数

描述 该函数等待ID为PID的子进程终止,返回已故进程的进程ID。如果PID不存在,则返回-1。进程的退出状态包含在$?中。 可以将标志设置为各种值,这些值等于waitpid()UNIX系统调用使用的值。 FLAGS的值为0应该在支持进程的所有操作系统上工作。 语法 以下是此函数的简单语法- …...

Redis之缓存雪崩、缓存击穿、缓存穿透问题

文章目录 前言一、缓存雪崩1.1、原因分析2.2、常用解决方案 二、缓存击穿2.1、原因分析2.2、常用解决方案2.2.1、使用互斥锁2.2.2、逻辑过期方案2.3、方案对比 三、缓存穿透3.1、原因分析3.2、解决方案3.2.1、缓存空对象3.2.3、布隆过滤3.3、方案对比 总结 前言 本文谈谈Redis…...

九五从零开始的运维之路(其三十五)

文章目录 前言一、概述1.概念2.组成3.特点4.工作原理5.优点&#xff1a; 二、各节点及其ip地址三、构建MHA1.ssh免密登录2.构建mysql主从复制&#xff08;一&#xff09;安装mariadb数据库并启动&#xff08;二&#xff09;master服务器&#xff08;三&#xff09;slave服务器&…...

5G科技防汛,助力守护一方平安

“立秋虽已至&#xff0c;炎夏尚还在”&#xff0c;受台风席卷以及季节性影响全国多地正面临强降水的严峻挑战。“落雨又顺秋&#xff0c;绵绵雨不休”&#xff0c;正值“七下八上” 防汛关键时期&#xff0c;贵州省水文水资源局已全面进入备战状态。 为确保及时响应做好防汛抢…...

用easyui DataGrid编辑树形资料

easyui显示编辑树形资料有TreeGrid元件&#xff0c;但是这个元件的vue版本和react版本没有分页功能。virtual scroll功能也表现不佳。 我用DataGrid来处理。要解决的问题点&#xff1a; &#xff08;1&#xff09;如何显示成树形。即&#xff0c;子节点如何有缩进。 先计算好…...

Azure存储账户

存储账户的概念 Azure存储账户是Azure提供的一种云存储解决方案&#xff0c;用于存储和访问各种类型的数据&#xff0c;包括文件、磁盘、队列、表格和Blob&#xff08;二进制大对象&#xff09;数据。存储账户可以基于访问模式和冗余需求来选择不同的类型&#xff0c;以满足应…...

数字人服装布料解算技术服务,让数字人驱动更真实

一个数字人通过三维建模、骨骼绑定、表情绑定后&#xff0c;对于数字人有两种使用场景&#xff0c;可以使用动捕设备实时驱动&#xff0c;将静态的3D模型结合动捕设备实时“活”起来。数字人通过动捕设备实时驱动的过程&#xff0c;则是基于实时布料毛发解算方案进行技术处理的…...

达梦数据库安装与初始化超详细教程

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-达梦数据库&#xff08;主要讲一些达梦数据库相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下达梦数据库的下载与安装教程&#xff08;Windows版&am…...

vue输入框只能输入数字类型,禁止输入和粘贴e

js怎么去除1e里面e 方法一&#xff1a;使用 Number() 函数将科学计数法表示的字符串转换为数字。然后&#xff0c;使用 toString() 方法将其转换回字符串形式&#xff0c;这样就会自动移除科学计数法中的 "e" var num 1e10; // 科学计数法表示的数字 var numStr …...

金盘 微信管理平台 getsysteminfo 未授权访问漏洞[2023-HW]

金盘 微信管理平台 getsysteminfo 未授权访问漏洞 一、漏洞描述二、漏洞影响三、网络测绘四、漏洞复现小龙POC检测: 五、 修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后…...

02-前端基础第二天-HTML5

01-HTML标签&#xff08;下&#xff09;导读 目标&#xff1a; 能够书写表格能够写出无序列表能够写出3~4个常用input表单类型能够写出下拉列表表单能够使用表单元素实现注册页面能够独立查阅W3C文档 目录&#xff1a; 表格标签列表标签表单标签综合案例查阅文档 02-表格标…...

i18n 配置vue项目中英文语言包(中英文转化)

一、实现效果 二、下载插件创建文件夹 2.1 下载cookie来存储 npm install --save js-cookienpm i vue-i18n -S 2.2 封装组件多页面应用 2.3 创建配置语言包字段 三、示例代码 3.1 main.js 引用 i18n.js import i18n from ./lang// 实现语言切换:i18n处理element&#xff0c…...

NFTScan NFT API 在 DID Protocol 开发中的应用

自互联网发展以来&#xff0c;Web2.0 时代产生了网络社会&#xff0c;社会已经不再局限于地理边界&#xff0c;而 Web 3.0 引入了去中心化的理念&#xff0c;强调个体数据隐私和可信互操作性。在这个新的时代中&#xff0c;去中心化身份&#xff08;Decentralized Identifier 即…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 原创笔记&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;《数据结构第4章 数组和广义表》…...

鸿蒙HarmonyOS 5军旗小游戏实现指南

1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;采用DevEco Studio实现&#xff0c;包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...

【Java基础】​​向上转型(Upcasting)和向下转型(Downcasting)

在面向对象编程中&#xff0c;转型&#xff08;Casting&#xff09; 是指改变对象的引用类型&#xff0c;主要涉及 继承关系 和 多态。 向上转型&#xff08;Upcasting&#xff09; ⬆️ 定义 将 子类对象 赋值给 父类引用&#xff08;自动完成&#xff0c;无需强制转换&…...