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

【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

http://www.mrgr.cn/news/82652.html

相关文章:

  • ElasticSearch基础-文章目录
  • Infineon PSoC 4 CapSense ModusToolbox IDE - 系统生态篇
  • 【C#】C# 使用onnxruntime报错记录
  • .net core修行之路-多线程异步编程概念篇
  • 【光纤通信】SONET 和 SDH——电路交换光网络
  • 什么是Kafka的重平衡机制?
  • Linux 基础七 内存
  • 修改secure-file-priv参数-mysql5.7.26限制不允许导入或导出的解决方法
  • GNU链接器简介-2
  • Ubuntu 下载安装 Consul1.17.1
  • Branch-Solve-Merge Improves Large Language Model Evaluation and Generation
  • 高中数学部分基础知识
  • 【51单片机零基础-chapter4:LED数码管】
  • 【51单片机零基础-chapter2:灯独立点亮,自定义点亮,跑马灯点亮,函数】
  • 1.1.3 插入排序
  • GNU链接器简介
  • MAC环境安装(卸载)软件
  • 数据结构C语言描述8(图文结合)--哈希、哈希冲突、开放地址法、链地址法等实现
  • Oracle Dataguard(主库为 Oracle 11g 单节点)配置详解(2):配置主数据库
  • Kraft模式安装Kafka(含常规、容器两种安装方式)
  • 【操作系统不挂科】操作系统期末考试题库<1>(单选题&简答题&计算与分析题&应用题)
  • ARM CCA机密计算安全模型之固件更新
  • 代码实战:基于InvSR对视频进行超分辨率重建
  • Unity-Mirror网络框架-从入门到精通之Benchmark示例
  • 1.1.2.1 选择 + 冒泡排序
  • Oracle 11g rac + Dataguard 环境调整 redo log 大小