rust 函数-生命周期
记录一下自己理解的生命周期。
每个变量都有自己的生命周期。
在c++里生命周期好比作用域, 小的作用域的可以使用大作用域的变量。 如果把这里的每个作用域取个名,那么就相当于rust里的生命周期注解。
拿例子说事一:
如果按照c++的方式来理解, 这个x和r的作用域是一样的,都是在main函数中。
但是如果按照rust生命周期去理解, x的生命周期比r的生命周期大。 因为a在main的第一行. b的在main中的第二行。
有变量的生命周期是一样的,那就是元组结构. 类似这样。 let (a,b,c) = tuple;
rust不允许, let a=1,b=1; 这样定义变量(c/c++/js风格)
也不允许 let a,b=4,1; 这样定义变量, (lua风格)
在这里其实'a 和 'b的生命周期 在编译器看来是不一样的,这一点一定要记住。 但是可以调用同一生命周期的函数,因为 'b生命周期包含'a 生命周期。
编译器处理其实是按照'a生命周期(公共的部分,也可以理解为使用较短生命周期)代入函数中的。 某个变量的生命周期是开始定义变量开始,到最后一次使用变量。 这里可能设计变量租借延续生命周期。
拿例子说事二:
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
}
}
如果用c++的视角(编码风格)去看这个程序, 会觉得是一段很正常的代码,完全没毛病啊。
但是实际编译就会出错,错误提示就是和生命周期有关。
对技术这么严谨的我,内心问了一万个为什么?
我经常把自己想成编译器作者,自己问自己,如果你是作者你会怎么处理?
答: 每个函数都会检查每个变量的生命周期的范围。 对longest函数来说, 有两个参数,x,y 都是都是引用。 本身不占空间(4或8字节指针忽略)。
在编译longest的时候,x,y引用谁,编译器不清楚。 有一种办法是可以知道的,就是编译完整个rust项目代码,在回过头看这个。就可以知道了。
但是这种不现实,这种会造成编译器及其复杂,并且及其缓慢。 因为一个项目不只有只有一个loggest, 有成千上万类似这样的,互相交错调用。
得来回反复的编译检查才能确定引用的谁,生命周期如何,这种是不确定的,x可能引用的东东生命周期长,可能短, 如果把函数结果赋值给属于其他生命周期的变量,就可能存在安全隐患了,c++是可以这么干的,但是开发者必须清楚指针指向的哪,引用的谁,否则程序崩了,不要怪谁,就是开发者垃圾。但是事实上,再牛逼的程序员,也会写出有bug的程序。
rust就是为安全而生的, 编译器帮你排除内存隐患,并发隐患。完成这个目标可不容易,需要开发者协助完成( 导致学习难度有点大,主要卡在思维上。 开发者不要随心所欲)
说了这么多,这个核心问题就是:
粗鲁点解释: 老子(rustc)不知道你指向的东西,和你的函数结果要返回给哪个,不告诉我,老子我就不让你过。
专业点解释: 因为rustc不知道 x
和 y
的生命周期是如何与返回值的生命周期相关联的
开发者独白: 老子该怎么告诉你(rustc)啊?
答:增加泛型生命周期参数来定义引用间的关系以便借用检查器可以进行分析
生命周期注解语法
生命周期注解并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期注解描述了多个引用生命周期相互的关系,而不影响其生命周期。
生命周期注解有着一个不太常见的语法:生命周期参数名称必须以撇号('
)开头,其名称通常全是小写,类似于泛型其名称非常短。'a
是大多数人默认使用的名称。生命周期参数注解位于引用的 &
之后,并有一个空格来将引用类型与生命周期注解分隔开。
这里有一些例子:我们有一个没有生命周期参数的 i32
的引用,一个有叫做 'a
的生命周期参数的 i32
的引用,和一个生命周期也是 'a
的 i32
的可变引用:
&i32 // 引用
&'a i32 // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用
单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系的。例如如果函数有一个生命周期 'a
的 i32
的引用的参数 first
。还有另一个同样是生命周期 'a
的 i32
的引用的参数 second
。这两个生命周期注解意味着引用 first
和 second
必须与这泛型生命周期存在得一样久。
注解只是一个名字,用单引号和标识符组成.
函数签名中的生命周期注解
就像泛型类型参数,泛型生命周期参数需要声明在函数名和参数列表间的尖括号中。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
现在函数签名表明对于某些生命周期 'a
,函数会获取两个参数,他们都是与生命周期 'a
存在的一样长的字符串 slice。函数会返回一个同样也与生命周期 'a
存在的一样长的字符串 slice。这就是我们告诉 Rust 需要其保证的契约。记住通过在函数签名中指定生命周期参数时,我们并没有改变任何传入后返回的值的生命周期。而是指出任何不遵守这个协议的传入值都将被借用检查器拒绝。注意 longest
函数并不需要知道 x
和 y
具体会存在多久,而只需要知道有某个可以被 'a
替代的作用域将会满足这个签名。
当在函数中使用生命周期注解时,这些注解出现在函数签名中,而不存在于函数体中的任何代码中。这是因为 Rust 能够分析函数中代码而不需要任何协助,不过当函数引用或被函数之外的代码引用时,让 Rust 自身分析出参数或返回值的生命周期几乎是不可能的。这些生命周期在每次函数被调用时都可能不同。这也就是为什么我们需要手动标记生命周期。
当具体的引用被传递给 longest
时,被 'a
所替代的具体生命周期是 x
的作用域与 y
的作用域相重叠的那一部分。换一种说法就是泛型生命周期 'a
的具体生命周期等同于 x
和 y
的生命周期中较小的那一个。因为我们用相同的生命周期参数 'a
标注了返回的引用值,所以返回的引用值就能保证在 x
和 y
中较短的那个生命周期结束之前保持有效。
以下代码正确:
fn main() {
let string1 = String::from("long string is long"); {
let string2 = String::from("xyz");
let 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
}
}
以下代码错误:
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
}
}
失败原因:
通过生命周期参数告诉 Rust 的是: longest
函数返回的引用的生命周期应该与传入参数的生命周期中较短那个保持一致。
因此,借用检查器不允许示例 10-24 中的代码,因为它可能会存在无效的引用。
longest函数结果的表明的生命周期应该与string2一致。 但是代码反馈的是result与string1一致。
可以使用租借(给string2续命,不要立即释放),修复错误:
fn main() {
let string1 = String::from("long string is long");
let result;
let zujie;
{
let string2 = String::from("xyz");
zujie = string2;
result = longest(string1.as_str(), zujie.as_str());
}
println!("The longest string is {}", result);
}
如果返回值和y没有关系,可以不用对y注解。
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
出现垂悬引用,代码不通过。 说明:这个可以通过使用有所有权的变量租借解决此问题。说白点,就是给返回值续命。 如果真这样解决,就不需要生命周期注解了。
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}
综上,生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的。一旦他们形成了某种关联,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为。
rust 函数-生命周期的更多相关文章
- React 函数生命周期
React 函数生命周期基础 1 ,概念 在组件创建.到加载到页面上运行.以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为组件的生命周期:* 2,组件生命周 ...
- 局部变量&&malloc函数&&生命周期的一些见解
最近在温习指针的部分时发现了一个有趣的问题,先看以下程序: //1.c #include<stdio.h> int* fun() { int t = 567; return &t; ...
- Vue钩子函数生命周期实例详解
vue生命周期简介 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程,我们称这是Vue的生命周期.通俗说就是Vue实例从创建到销毁 ...
- Vue系列之 => 钩子函数生命周期
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Unity 脚本函数生命周期
Awake(),一般我们在这里做一些组件的获得,比如使用getcomponent方法. Start(),我们可以在这里给变量赋值. FixUpdate(),固定更新,因为这里得更新速度为固定(可以在T ...
- 【vue】钩子函数生命周期
图1 图2: 图3 相关资料:http://www.zhimengzhe.com/Javascriptjiaocheng/236707.html https://segmentfault.com ...
- rust 生命周期2
之前定义的结构体,都是不含引用的. 如果想定义含引用的结构体,请定义生命周期注解 #[warn(unused_variables)] struct ImportantExcerpt<'a> ...
- 文盘Rust -- struct 中的生命周期
最近在用rust 写一个redis的数据校验工具.redis-rs中具备 redis::ConnectionLike trait,借助它可以较好的来抽象校验过程.在开发中,不免要定义struct 中的 ...
- Unity脚本中各函数成员的生命周期
在学习Unity时,掌握如何编写脚本是必须掌握的一项基本技能.但是关于Unity的游戏脚本中各函数的生命周期是怎样开始和结束的,它们的执行顺序是如何安排的?这一点我们要清楚的了解. 我们知道Unity ...
随机推荐
- Mysql-NULL转数字
最近做了一个学生成绩表,其中遇到一个小问题 需要统计个门科目的平均成绩,在统计到高等数学时,因为高数没有人考,在成绩表中根本不存在的分数,但是在课程表存在高数科目. 当这两个表内联然后统计分数,这样会 ...
- npm audit fix 报错
found 2504 vulnerabilities (1360 low, 1109 moderate, 29 high, 6 critical) run `npm audit fix` to fi ...
- vue项目中使用bpmn-番外篇(留言问题总结)
前情提要 “vue项目中使用bpmn-xxxx”系列的七篇文章在上周已经更新完成,发表后,有小伙伴在使用时提出了一些文章中没有讲到的问题,此篇作为番外篇,将大家提出的共性问题解答一下,欢迎大家支持原创 ...
- 【JavaScript数据结构系列】00-开篇
[JavaScript数据结构系列]00-开篇 码路工人 CoderMonkey 转载请注明作者与出处 ## 0. 开篇[JavaScript数据结构与算法] 大的计划,写以下两部分: 1[JavaS ...
- 【C++】常见易犯错误之数值类型取值溢出与截断(2)
本节内容紧接上节,解决红色字体遗留问题.本节所有例子运行环境: win10 + VS2015 + X64 + debug 在上节例子中,查看变量 c .d .d+1 的类型. //// Console ...
- Parrot os配置源更新
每次都是忘了怎么配置,去官网查文档,这记一下 一.源文件配置注意 首先要注意Parrot官方软件库的默认更新源文件不在 /etc/apt/sources.list 而是 /etc/apt/source ...
- 个人工具,编辑器visual studio code
个人收集的使用方法:简化版 主要基于基础web前端开发,visual studio code教程——基础使用.扩展插件安装使用 下载地址: https://visualstudio.microsoft ...
- jchdl - GSL值的传播
https://mp.weixin.qq.com/s/jgMljoca-Cwe9x0NaTLzZg GSL的拓扑模型是线和节点连接的模型,值的传播,即是值在线和节点之间传播和转化的过程. 值的 ...
- (Java实现) 蓝桥杯 国赛 重复模式
标题:重复模式 作为 drd 的好朋友,技术男 atm 在 drd 生日时送给他一个超长字符串 S .atm 要 drd 在其中找出一个最长的字符串 T ,使得 T 在 S 中至少出现了两次,而他想说 ...
- Java实现 蓝桥杯 算法提高 概率计算
算法提高 概率计算 时间限制:1.0s 内存限制:256.0MB 问题描述 生成n个∈[a,b]的随机整数,输出它们的和为x的概率. 输入格式 一行输入四个整数依次为n,a,b,x,用空格分隔. 输出 ...