【Rust学习笔记】Rust生命周期参数的详细阐述
原创 acodercat Rust语言中文社区 前天
https://mp.weixin.qq.com/s/XpX-BN__R9ylLZVjmEnx5g
Rust生命周期

程序中每个变量都有一个固定的作用域,当超出变量的作用域以后,变量就会被销毁。变量在作用域中从初始化到销毁的整个过程称之为生命周期。

rust的每个函数都会有一个作用域,也可以在函数中使用一对花括号再内嵌一个作用域。比如如下代码中就在main函数的函数作用域中又内嵌了一个作用域:

fn main() {let a;       // --------------+-- a start{            //               |let b = 5; // -+-- b start  |}            // -+-- b over   |
}              // --------------+-- a over

上面代码存在两个作用域,一个是main函数本身的作用域,另外一个是在main函数中使用一对{}定义了一个内部作用域。第2行代码声明了变量a,它的作用域是整个main函数,也可以说它的生命周期是从第2行代码到第6行代码。在第4行代码中声明了变量b,它的作用域是第4行到第6行。我们可以发现变量的生命周期是有长短的。
生命周期与借用

rust中的借用是指对一块内存空间的引用。rust有一条借用规则是借用方的生命周期不能比出借方的生命周期还要长。

例如:

fn main() {let a;                // -------------+-- a start{                     //              |let b = 5;          // -+-- b start |a = &b;             //  |           |}                     // -+-- b over  |println!("a: {}", a); //              |
}                       // -------------+-- a over

上面第5行代码把变量b借给了变量a,所以a是借用方,b是出借方。可以发现变量a(借用方)的生命周期比变量b(出借方)的生命周期长,于是这样做违背了rust的借用规则(借用方的生命周期不能比出借方的生命周期还要长)。因为当b在生命周期结束时,a还是保持了对b的借用,就会导致a所指向的那块内存空间已经被释放了,那么变量a就会是一个悬垂引用。
运行上面代码会报如下错误:

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

意思就是说变量b的生命周期不够长。变量b已经被销毁了仍然对它进行了借用。
一个正确的例子:

fn main() {let a = 1;            // -------------+-- a startlet b = &a;           // -------------+-- b startprintln!("a: {}", a); //              |
}                       // -------------+-- b, a over

观察上面代码发现变量b(借用方)的生命周期要比变量a(出借方)的生命周期要短,所以借用检查器会通过。
函数中的生命周期参数

对于一个参数和返回值都包含引用的函数而言,该函数的参数是出借方,函数返回值所绑定到的那个变量就是借用方。所以这种函数也需要满足借用规则(借用方的生命周期不能比出借方的生命周期还要长)。那么就需要对函数返回值的生命周期进行标注,告知编译器函数返回值的生命周期信息。

我们下面定义一个函数,该函数接收两个i32的引用类型,返回大的那个数的引用。
示例:

fn max_num(x: &i32, y: &i32) -> &i32 {if x > y {&x} else {&y}
}fn main() {let x = 1;                // -------------+-- x startlet max;                  // -------------+-- max start{                         //              |let y = 8;              // -------------+-- y startmax = max_num(&x, &y);  //              |}                         // -------------+-- y overprintln!("max: {}", max); //              |
}                           // -------------+-- max, x over

由于缺少生命周期参数,编译器不知道max_num函数返回的引用生命周期是什么,所以运行报错:

error[E0106]: missing lifetime specifier--> src/main.rs:1:33|
1 | fn max_num(x: &i32, y: &i32) -> &i32 {|               ----     ----     ^ 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 `

函数的生命周期参数声明在函数名后的尖括号<>里,然后每个参数名跟在一个单引号’后面,多个参数用逗号隔开。如果在参数和返回值的地方需要使用生命周期进行标注时,只需要在&符号后面加上一个单引号’和之前声明的参数名即可。生命周期参数名可以是任意合法的名称。例如:

fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {if x > y {&x} else {&y}
}
fn main() {let x = 1;                // -------------+-- x startlet max;                  // -------------+-- max start{                         //              |let y = 8;              // -------------+-- y startmax = max_num(&x, &y);  //              |}                         // -------------+-- y overprintln!("max: {}", max); //              |
}                           // -------------+-- max, x over

上面代码对函数max_num的参数和返回值的生命周期进行了标注,用于告诉编译器函数参数和函数返回值的生命周期一样长。在第13行代码对max_num进行调用时,编译器会把变量x的生命周期和变量y的生命周期与max_num函数的生命周期参数’a建立关联。这里值得注意的是,变量x和变量y的生命周期长短其实是不一样的,那么关联到max_num函数的生命周期参数’a的长度是多少呢?实际上编译器会取变量x的生命周期和变量y的生命周期重叠的部分,也就是取最短的那个变量的生命周期与’a建立关联。这里最短的生命周期是变量y,所以’a关联的生命周期就是变量y的生命周期。

运行上面代码,会有报错信息:

error[E0597]: `y` does not live long enough--> src/main.rs:13:27|
13 |         max = max_num(&x, &y);|                           ^^ borrowed value does not live long enough
14 |     }|     - `y` dropped here while still borrowed
15 |     println!("max: {}", max);|                         --- borrow later used here

报错信息说变量y的生命周期不够长,当y的生命周期结束后,仍然被借用。
我们仔细观察发现max_num函数返回值所绑定到的那个变量max(借用方)的生命周期是从第10行代码到第16行代码,而max_num函数的返回值(出借方)的生命周期是’a,'a的生命周期又是变量x的生命周期和变量y的生命周期中最短的那个,也就是变量y的生命周期。变量y的生命周期是代码的第12行到第14行。所以这里不满足借用规则(借用方的生命周期不能比出借方的生命周期还要长)。也就是为什么编译器会说变量y的生命周期不够长的原因了。函数的生命周期参数并不会改变生命周期的长短,只是用于编译来判断是否满足借用规则。
将代码做如下调整,使其变量max的生命周期小于变量y的生命周期,编译器就可以正常通过:

fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {if x > y {&x} else {&y}
}
fn main() {let x = 1;                  // -------------+-- x startlet y = 8;                  // -------------+-- y startlet max = max_num(&x, &y);  // -------------+-- max startprintln!("max: {}", max);   //              |
}                             // -------------+-- max, y, x over

函数存在多个生命周期参数时,需要标注各个参数之间的关系。例如:

fn max_num<'a, 'b: 'a>(x: &'a i32, y: &'b i32) -> &'a i32 {if x > y {&x} else {&y}
}
fn main() {let x = 1;                  // -------------+-- x startlet y = 8;                  // -------------+-- y startlet max = max_num(&x, &y);  // -------------+-- max startprintln!("max: {}", max);   //              |
}                             // -------------+-- max, y, x over

上面代码使用’b: 'a来标注’a与’b之间的生命周期关系,它表示’a的生命周期不能超过’b,即函数返回值的生命周期’a(借用方)不能超过’b``(出借方),'a也不会超过’a`(出借方)。
结构体中的生命周期参数

一个包含引用成员的结构体,必须保证结构体本身的生命周期不能超过任何一个引用成员的生命周期。否则就会出现成员已经被销毁之后,结构体还保持对那个成员的引用就会产生悬垂引用。所以这依旧是rust的借用规则即借用方(结构体本身)的生命周期不能比出借方(结构体中的引用成员)的生命周期还要长。因此就需要在声明结构体的同时也声明生命周期参数,同时对结构体的引用成员进行生命周期参数标注。

结构体生命周期参数声明在结构体名称后的尖括号<>里,每个参数名跟在一个单引号’后面,多个参数用逗号隔开。在进行标注时,只需要在引用成员的&符号后面加上一个单引号’和之前声明的参数名即可。生命周期参数名可以是任意合法的名称。例如:

struct Foo<'a> {v: &'a i32
}

上面代码可以把结构体Foo的生命周期与成员v的生命周期建立一个关联用于编译器进行借用规则判断。
函数生命周期参数要注意一点的是,如果函数的参数与函数的返回值不建立生命周期关联的话,生命周期参数就毫无用处。
下面是一个违反借用规则的例子:

#[derive(Debug)]
struct Foo<'a> {v: &'a i32
}fn main() {let foo;                    // -------------+-- foo start{                           //              |let v = 123;              // -------------+-- v startfoo = Foo {               //              |v: &v                   //              |}                         //              |}                           // -------------+-- v overprintln!("foo: {:?}", foo); //              |
}                             // -------------+-- foo over

上面代码的第14行到15行foo的生命周期依然没有结束,但是它所引用的变量v已经被销毁了,因此出现了悬垂引用。编译器会给出报错提示:变量v的的生命周期不够长。
静态生命周期参数

有一个特殊的生命周期参数叫static,它的生命周期是整个应用程序。跟其他生命周期参数不同的是,它是表示一个具体的生命周期长度,而不是泛指。static生命周期的变量存储在静态段中。

所有的字符串字面值都是 'static 生命周期,例如:

let s: &'static str = "codercat is a static lifetime.";

上面代码中的生命周期参数可以省略,就变成如下形式:

let s: &str = “codercat is a static lifetime.”;

还有static变量的生命周期也是’static。
例如:

static V: i32 = 123;

下面举一个特殊的例子:

fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {if x > y {&x} else {&y}
}
fn main() {let x = 1;                // -------------+-- x startlet max;                  // -------------+-- max start{                         //              |static Y: i32 = 8;      // -------------+-- Y startmax = max_num(&x, &Y);  //              |}                         //              |println!("max: {}", max); //              |
}                           // -------------+-- max, Y, x over

还是之前的max_num函数。在代码的第12行定义了一个静态变量,它的生命周期是’static 。max_num函数的生命周期参数’a会取变量x的生命周期和变量Y的生命周期重叠的部分。所以传入max_num函数并不会报错。
总结
以上内容是我个人在学习rust生命周期参数相关内容时的总结,如有错误欢迎指正。文中的借用和引用实际上是一个东西。

【Rust学习笔记】Rust生命周期参数的详细阐述相关推荐

  1. day4 vue 学习笔记 组件 生命周期 数据共享 数组常用方法

    系列文章目录 day1学习vue2笔记 vue指令 day2 学习vue2 笔记 过滤器 侦听器 计算属性 axios day3 vue2 学习笔记 vue组件 day4 vue 学习笔记 组件 生命 ...

  2. IOS学习笔记——ViewController生命周期详解

    在我之前的学习笔记中讨论过ViewController,过了这么久,对它也有了新的认识和体会,ViewController是我们在开发过程中碰到最多的朋友,今天就来好好认识一下它.ViewContro ...

  3. react学习笔记(8)生命周期回顾与再认识

    生命周期 生命周期是一个组件从创建到销毁的过程. 当组建实例被创建并且插入到DOM中,需要调用的函数,就是生命周期函数. 也就是说,组件加载完成前后.组件更新数据.组件销毁,所触发的一系列的方法. 1 ...

  4. rust学习笔记-rust语言基础

    rust语言基础 rust语言的优点 c/c++ 性能好,但是类型系统和内存都不太安全. java/c#, 有GC,内存安全有很多优秀的特性,但性能不行. Rust:安全,无需GC,易于维护 rust ...

  5. android学习笔记---36_Activity生命周期

    36_Activity生命周期 ----------------------------- 1.Activity生命周期,用于activity在运行时候受到一些突然事件的影响   ,例如:正在使用一个 ...

  6. Gavin小黑屋——Vue 学习笔记 :生命周期特点(先渲染HTML标签再渲染数据)

    Vue基础   生命周期特点(先渲染HTML标签再渲染数据) 目录 Vue基础   生命周期特点(先渲染HTML标签再渲染数据) 一.Vue生命周期 Vue 的生命周期总共分为8个阶段:创建前/后,载 ...

  7. rust笔记11 生命周期引用有效性

    函数参数等的生命周期 rust保证内存安全的手段之一是没有悬空的引用(指针),而这个靠编译期间强制生命周期检查实现.先给出生命周期的声明方式: &i32 // i32的引用 &'a i ...

  8. rust学习笔记中级篇1–泛型(霜之小刀)

    rust学习笔记中级篇1–泛型(霜之小刀) 欢迎转载和引用,若有问题请联系 若有疑问,请联系 Email : lihn1011@163.com QQ:2279557541 结构体泛型 首先上代码,如何 ...

  9. Unity3D学习笔记:粒子特效参数

    Unity3D学习笔记:粒子特效参数含义 转载 https://blog.csdn.net/asd237241291/article/details/8433534 粒子特效 粒子系统检视面板 初始化 ...

  10. python学习笔记(14)参数对应

    python学习笔记(14)参数对应 原链:http://www.cnblogs.com/vamei/archive/2012/07/08/2581264.html 笔记: 1 #第14讲 2 #参数 ...

最新文章

  1. java 对象被回收的例子_Java对象的后事处理——垃圾回收(二)
  2. 武汉大学:全校本科毕业论文答辩,以线上方式进行!
  3. oracle导入excel字段超过4000字符数据_产品思考:B端产品中,为什么批量导入功能很重要?...
  4. 不懂AI的我,是如何搞开发的?
  5. property、staticmethod、classmethod与__str__的用法
  6. gravity、layout_gravity及orientation
  7. springboot几种注入_Spring Boot中使用JdbcTemplate访问数据库
  8. ASP.NET Core on K8S深入学习(11)K8S网络知多少
  9. python包实际怎么使用_Python--模块与包
  10. java ee maven_针对新手的Java EE7和Maven项目–第8部分
  11. 计算机网络 socket阻塞非阻塞
  12. 微信网页授权只能配置一个域名
  13. excel 行列转换
  14. 非接触物体尺寸形态测量(G 题)
  15. 计算机网络和智能家居,华中科技大学计算机网络专题智能家居与普适计算.ppt...
  16. 微信小程序android和IOS拨打电话区别
  17. ubuntu安装以及环境配置
  18. em表示什么长度单位_css中pt、px、em、ex、in等这类长度单位详细说明
  19. 不同的靶材(Cu,Cr,Co,Mo,Fe靶)对XRD谱有什么影响
  20. Unity的AB包系统使用概论

热门文章

  1. Vue 双向数据绑定原理分析
  2. 一个很小的 截图 库。 只需要依赖 jQuery
  3. cownew开源-cownewStudio抢先预览
  4. Notepad++-第一篇命令行语句执行之编译、运行Java
  5. Python中系统命令
  6. Python开发【第五篇】:Python基础之杂货铺
  7. 20145233《网络对抗》Exp6 信息收集和漏洞扫描
  8. 两个android程序间的相互调用(apk互调)
  9. 2020-12-26
  10. 2020-11-30 OpenCV人工智能图像处理学习笔记 第3章 计算机视觉加强之几何变换 warpAffine