rust学习-探讨为什么需要标注生命周期

rust学习-探讨为什么需要标注生命周期

  • 生命周期标注到底在做什么?
    • 原始代码(没有标注)
    • 添加标注后
  • 关键:标注是契约,不是控制
    • 例子1:标注如何帮助编译器验证
  • 更直观的比喻:租房合同
  • 生命周期标注的实际作用:建立和验证关系
    • 作用1:告诉编译器引用的来源关系
    • 作用2:告诉编译器生命周期之间的关系
  • 验证实验:看看标注的实际效果
    • 实验1:去掉标注会怎样?
    • 实验2:看看编译器如何推理
  • 生命周期标注的智能之处:自动推断
  • 实际案例:看看标注如何防止bug
    • 案例1:新闻摘要系统
    • 正确的版本:
  • 生命周期标注的本质:一种类型系统
  • 总结:生命周期标注的真正作用

前要说明:生命周期标注不改变实际生命周期

一个引用的实际生命周期是由代码的写法(作用域)决定的,标注不改变它。就像一个人的寿命由他/她的生活决定,而不是由身份证上的出生日期决定。

生命周期标注到底在做什么?

可以用 “身份证系统” 来比喻:

原始代码(没有标注)

fnget_shorter(x:&str,y:&str)->&str{ifx.len()<y.len(){x}else{y}}

编译器看到这个函数会说:“我不知道你返回的引用来自哪里,也不知道它应该活多久,我无法验证调用方是否安全。”

添加标注后

fnget_shorter<'a>(x:&'astr,y:&'astr)->&'astr{ifx.len()<y.len(){x}else{y}}

现在函数签名告诉编译器:
"嘿,我接受两个引用 x 和 y,它们都有相同的生命周期 'a,我返回的引用也有这个相同的生命周期 'a。"

关键:标注是契约,不是控制

可以用几个例子展示标注的验证作用:

例子1:标注如何帮助编译器验证

fnmain(){letstring1=String::from("长的字符串");letresult;{letstring2=String::from("短");// string2 生命周期短result=get_shorter(&string1,&string2);// ① 编译器会在这里报错!}// string2 在这里被丢弃println!("最短的是: {}",result);// ② 但 result 可能指向 string2!}

编译器的推理过程:
1.在 ① 处调用 get_shorter
2.函数签名说:返回的引用和两个参数有相同的生命周期
3.参数的共同生命周期是 string1 和 string2 中较短的那个(即 string2 的生命周期)
4.所以 result 最多只能活到 string2 的作用域结束
5.在 ② 处使用 result 时,string2 已经死了
6.编译错误:可能使用悬垂引用!

如果不标注生命周期,编译器无法做出这个推理!

更直观的比喻:租房合同

想象一下租房场景:

// 类比:房东和租客的关系structHouse{address:String,}structTenant<'a>{// 'a 表示租期house:&'aHouse,// 租客只能住在这段时间内name:String,}// 合同:租客的租期不能超过房子的所有权期fnrent_house<'a>(house:&'aHouse,name:String)->Tenant<'a>{Tenant{house,name}}

实际的场景:

fnmain(){lethouse=House{address:"123 Main St".to_string()};// 房子存在lettenant={// 租客只能在这个作用域内租房子rent_house(&house,"Alice".to_string())};// 租客离开,但房子还在println!("房子地址: {}",house.address);// OK}

非法的场景:

fnmain(){lettenant;{lethouse=House{address:"123 Main St".to_string()};// 临时房子tenant=rent_house(&house,"Alice".to_string());// 错误!}// 房子被拆了// 租客还想住?// println!("租客住址: {}", tenant.house.address); // 不可能!}

生命周期标注的实际作用:建立和验证关系

作用1:告诉编译器引用的来源关系

// 返回的引用来自哪个参数?fnambiguous(x:&str,y:&str)->&str;// 不知道来自 x 还是 yfnfrom_x<'a>(x:&'astr,y:&str)->&'astr;// 明确来自 xfnfrom_y<'a>(x:&str,y:&'astr)->&'astr;// 明确来自 y

作用2:告诉编译器生命周期之间的关系

// 三个引用必须有什么关系?fncomplex<'a,'b,'c>(x:&'astr,y:&'bstr,z:&'cstr)->&'astrwhere'b:'a,// y 的生命周期包含 x 的生命周期'c:'b,// z 的生命周期包含 y 的生命周期{println!("y: {}, z: {}",y,z);x}

验证实验:看看标注的实际效果

实验1:去掉标注会怎样?

// 尝试1:没有标注 - 编译错误// fn longest(x: &str, y: &str) -> &str {// if x.len() > y.len() { x } else { y }// }// 尝试2:添加标注 - 编译通过fnlongest<'a>(x:&'astr,y:&'astr)->&'astr{ifx.len()>y.len(){x}else{y}}fnmain(){lets1="hello";lets2="world";letresult=longest(s1,s2);println!("{}",result);}

实验2:看看编译器如何推理

fnmain(){lets1=String::from("Rust");letresult;{lets2=String::from("C++");result=longest(&s1,&s2);// 编译器推理:// 1. s1 的生命周期:整个 main 函数// 2. s2 的生命周期:内部作用域// 3. 共同生命周期 'a 是两者的交集 = s2 的生命周期// 4. 所以 result 只能活到 s2 的作用域结束println!("内部: {}",result);// OK}// s2 被丢弃,result 理论上也失效// println!("外部: {}", result); // 编译错误!// 错误信息:`s2` does not live long enough}

生命周期标注的智能之处:自动推断

Rust 编译器有生命周期省略规则,很多情况不需要标注:

// 规则1:每个引用参数有自己的生命周期// fn first_word(s: &str) -> &str// 被推断为:fn first_word<'a>(s: &'a str) -> &'a str// 规则2:如果只有一个输入生命周期,它被赋给所有输出生命周期// fn only_param(s: &str) -> &str// 被推断为:fn only_param<'a>(s: &'a str) -> &'a str// 规则3:方法中 &self 的生命周期赋给所有输出生命周期structPerson{name:String,}implPerson{// 自动推断为:fn name<'a>(&'a self) -> &'a strfnname(&self)->&str{&self.name}}

实际案例:看看标注如何防止bug

案例1:新闻摘要系统

// 没有生命周期标注的bug版本(假设Rust允许)structNewsSummary{title:String,excerpt:&str,// 缺少生命周期标注!}implNewsSummary{fnnew(title:String,content:&str)->NewsSummary{letexcerpt=ifcontent.len()>100{&content[0..100]}else{content};NewsSummary{title,excerpt}}}// 使用这个结构体会出问题:fnmain(){letsummary;{letarticle=String::from("这是一篇很长的文章...");summary=NewsSummary::new("标题".to_string(),&article);}// article 被丢弃// 但 summary.excerpt 还指向 article 的内存!// println!("摘要: {}", summary.excerpt); // 使用已释放的内存!}

正确的版本:

structNewsSummary<'a>{title:String,excerpt:&'astr,// 标注:摘要和原文有相同的生命周期}impl<'a>NewsSummary<'a>{fnnew(title:String,content:&'astr)->NewsSummary<'a>{letexcerpt=ifcontent.len()>100{&content[0..100]}else{content};NewsSummary{title,excerpt}}}fnmain(){letarticle=String::from("这是一篇很长的文章...");letsummary=NewsSummary::new("标题".to_string(),&article);// article 必须比 summary 活得久println!("标题: {}",summary.title);println!("摘要: {}",summary.excerpt);// 下面的代码会编译错误:// let bad_summary;// {// let temp_article = String::from("临时文章");// bad_summary = NewsSummary::new("标题".to_string(), &temp_article);// } // temp_article 被丢弃// println!("{}", bad_summary.excerpt); // 错误!}

生命周期标注的本质:一种类型系统

把生命周期标注看作类型系统的一部分可能更容易理解:

// 普通类型系统fnadd(x:i32,y:i32)->i32{x+y}// 带有生命周期的类型系统fnlongest<'a>(x:&'astr,y:&'astr)->&'astr{...}// 类比:// i32, String 等是"值类型"// 'a, 'b 等是"生命周期类型"// &'a str 是"带有生命周期信息的引用类型"

总结:生命周期标注的真正作用

1. 不是改变生命周期:实际生命周期由代码作用域决定
2. 是建立契约:告诉编译器引用之间的关系
3. 是验证工具:让编译器检查代码是否符合安全规则
4. 是文档:告诉其他开发者你的意图

简单记忆:

  • 实际生命周期 = 代码怎么写(作用域)
  • 生命周期标注 = 告诉编译器"我认为这些引用应该有什么关系"
  • 编译器 = 检查"你的想法(标注)是否符合实际(代码作用域)"

最终答案:
生命周期标注就像是给编译器的一份安全保证书:
“我保证返回的引用至少和这些参数活得一样长。”
然后编译器说:“好的,我来检查一下你的代码是否符合这个保证。”

所以标注不改变实际生命周期,但它让编译器有能力检查代码是否安全。没有这个检查,Rust 就无法在编译时保证内存安全,那就需要像其他语言一样依赖运行时检查(垃圾回收)了。

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

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

相关文章

Docker 之mysql从头开始——Docker下mysql安装、启动、配置、进入容器执行(查询)sql

一、Docker 之mysql安装配置 步骤一&#xff1a;拉取镜像 1. 查看是否包含已安装的mysql。 docker images | grep mysql 2. 如上图所示&#xff0c;我们有mysql镜像&#xff0c;所以不必对mysql镜像进行拉取&#xff0c;如若没有上图中的惊喜&#xff0c;使用如下命令进行拉取…

DeepSeek R1 简易指南:架构、本地部署和硬件要求

DeepSeek 团队近期发布的DeepSeek-R1技术论文展示了其在增强大语言模型推理能力方面的创新实践。该研究突破性地采用强化学习&#xff08;Reinforcement Learning&#xff09;作为核心训练范式&#xff0c;在不依赖大规模监督微调的前提下显著提升了模型的复杂问题求解能力。 技…

深入解析DDR内存原理、测试方法及在嵌入式系统中的应用

文章目录摘要1. DDR内存核心技术解析1.1 物理架构与存储原理1.2 关键时序参数2. DDR测试方法与工具链2.1 测试环境搭建2.2 MemTest86测试流程3. 嵌入式系统集成实践3.1 ARM Cortex-A内存控制器配置4. 完整测试代码实现5. 常见问题解决5.1 信号完整性问题6. 成果展示技术图谱摘要…

基于Python的智能房价分析与预测系统设计-计算机毕业设计源码+LW文档

摘 要 房地产市场一直在发展&#xff0c;房价问题引起社会广泛关注。分析房价很重要&#xff0c;购房者需要这些信息&#xff0c;投资者需要这些数据&#xff0c;房地产企业也需要参考这些结果。传统房价分析方法存在不足&#xff0c;主要依靠个人经验&#xff0c;使用数据量较…

CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现

操作&#xff1a; 根据CVE-2024-38819&#xff1a;Spring 框架路径遍历 PoC 漏洞搭建复现的靶场环境 拿到环境的源码使用docker搭建 cd vuln创建容器并启动 docker build -t cve-2024-38819-poc .docker run -d -p 8080:8080 --name cve-2024-38819-poc cve-2024-38819-poc注…

基于Python爬虫的网络小说热度分析django-计算机毕业设计源码+LW文档

摘 要 在数字化信息飞速发展的当下&#xff0c;网络文学市场规模不断扩大&#xff0c;网络小说热度分析对于了解读者喜好、优化平台运营具有重要意义。随着互联网技术的持续演进&#xff0c;海量的网络小说数据蕴含着丰富的读者行为和偏好信息&#xff0c;如何从中挖掘有价值的…

com.microsoft.sqlserversqljdbc4jar4.0 was not found产生原因及解决步骤

文章目录 问题 sqlserver 包找不到 报错原因分析 主要原因 解决方案 步骤 1&#xff1a;检查 pom.xml 中的依赖声明步骤 2&#xff1a;配置 Microsoft 的 Maven 仓库步骤 3&#xff1a;强制更新 Maven 依赖步骤 4&#xff1a;清理本地仓库缓存步骤 5&#xff1a;手动下载并安装…

2026年最新爆火!7款AI论文写作神器限时实测,一键生成文献综述与真实交叉引用

引言&#xff1a;DDL前24小时的绝望&#xff1f;这篇急救指南能救你 凌晨3点&#xff0c;电脑屏幕亮着未完成的论文初稿&#xff0c;文献综述还缺30篇引用&#xff0c;导师的修改意见堆了3页——这是不是你赶论文时的真实写照&#xff1f; 2026年学术圈已经卷到“用AI抢时间”…

com.mysql.cj.jdbc.exceptions.CommunicationsException Communications link failure 问题解决

前言: 一般这个报错大多是网络原因导致的&#xff0c;确保你不是网络问题再往下看 问题 在一个方法上&#xff08;该方法非常复杂执行时间长&#xff09;加了 Transactional(rollbackFor Exception.class)后出现了如下图所示的错误解决&#xff1a; 经过排查并非网络问题。复现…

【NLP】Hugging Face使用指南

文章目录一、Hugging Face介绍二、加载并使用预训练模型2.1 查找预训练模型2.2 实际案例2.2.1 调取预训练模型2.2.2 如何在具体的推理任务中使用预训练模型&#xff1f;2.3 如何在训练前就判定好哪些模型适用于实际任务&#xff1f;三、词嵌入工具与词嵌入模型3.1 调用分词器&a…

Git合并时忽略文件的6种技巧

有几种方法可以在 git merge 时忽略特定文件的合并&#xff1a;方法一&#xff1a;使用 .gitattributes 文件&#xff08;推荐&#xff09;在项目根目录创建或编辑 .gitattributes 文件&#xff1a;infostation.js mergeours *.js mergeours # 如果需要忽略所有js文件的合并然…

在 Ubuntu 下载 Typora

1.下载 Typora 安装包 鱼香ROS_Typora 安装地址https://fishros.org.cn/forum/topic/23/%E5%AE%89%E8%A3%85typora%E6%9C%80%E5%90%8E%E7%9A%84%E5%85%8D%E8%B4%B9%E7%89%88 2.不解压&#xff0c;下载 Typora 首先找到压缩包下载地址&#xff0c;右键空白处选择在终端中打开&…

RK3588+kylin V10安装docker

检查内核是否支持docker 方法&#xff1a;工具分享&#xff1a;检测内核配置是否支持Docker等容器 (1)检查卸载老版本Docker sudo apt-get remove docker docker-engine docker.io containerd runc (2)安装Docker依赖 sudo apt-get install ca-certificates curl gnupg lsb…

ATL80.dll文件丢失找不到 打不开问题 免费下载方法分享

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

atl110.dll文件丢失找不到 打不开问题 免费下载方法分享

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

最新爆火6款免费AI论文神器!PaperTan一站式搞定选题降重

紧急预警&#xff1a;论文截止只剩72小时&#xff1f;这些AI工具能救你命&#xff01; 凌晨3点的图书馆、满屏的文献标红、导师催改的第12条消息——如果你正在经历这些&#xff0c;现在就是你最后的急救机会&#xff01;2024年最新爆火的6款免费AI论文工具&#xff0c;能帮你…

深度测评专科生必备9款AI论文平台:2026最新对比与推荐

深度测评专科生必备9款AI论文平台&#xff1a;2026最新对比与推荐 2026年专科生论文写作工具测评&#xff1a;为何需要一份权威榜单&#xff1f; 随着AI技术的不断进步&#xff0c;越来越多的专科生开始借助AI工具提升论文写作效率。然而&#xff0c;面对市场上五花八门的平台&…

Linux 命令行实战训练营(

Linux 命令行实战训练营&#xff08;Linux Command Line Bootcamp&#xff09;课程基本信息- 发布时间&#xff1a;2026年1月 - 格式与规格&#xff1a;MP4 格式 | 视频 1920x1080 分辨率 - 语言&#xff1a;英语 - 时长&#xff1a;28 节课&#xff08;总计 4 小时 &#xff…