rust 生命周期很难理解,本文轻松帮你搞定!

编程入门 行业动态 更新时间:2024-10-26 08:31:57

rust 生命周期<a href=https://www.elefans.com/category/jswz/34/1770555.html style=很难理解,本文轻松帮你搞定!"/>

rust 生命周期很难理解,本文轻松帮你搞定!

1. 变量生命周期 —— rust 自动推断

一般而言,rust 在编译的时候能够自动推断变量的生命周期之间是否合理覆盖。如果变量 x 需要引用变量 y 的数据,那么 y 的生命周期必须覆盖 x。 不然的话,y 死掉了,x 引用的数据就成了非法的了。

然而,在有些时候,编译器的推理的线索会断掉,导致无法做出判断。例如下面这段代码就无法通过编译。

fn longer(s1: &str, s2: &str) -> &str {if s2.len() > s1.len() {s2} else {s1}
}fn main() {let r;let s1 = String::from("rust");let s2 = String::from("ecmascript");r = longer(&s1, &s2);println!("{} is longer", r);
}

原因是,函数 longer 的返回结果的生命周期,可能与 s1 相同,也可能与 s2 相同。因此,longer 结果的生命周期在编译阶段是未知的。

所以,编译器在分析到 r = longer(&s1, &s2); 这一行时,无法确定 r 得到的数据,其生命周期是否足够长。于是,编译器提醒我们加入相关说明,让编译器知晓如何计算 longer 返回结果的生命周期。

因此,rust 引入了生命周期标注,用来协助编译器推理。

有人说了,这段代码我觉得不用标注也能看出来,代码的生命周期没问题呀?编译器难道看不出来吗?确实如此,我们可以简单地这样理解。编译器只依据简单的规则依据输入参数的生命周期推断输出参数的生命周期。longer 函数包括两个 &T 类型的输入参数,返回结果也是 &T 类型。从函数声明看,无法确定返回结果到底与哪个输入变量相同。于是,要求我们添加生命周期标记。

2. 生命周期标志

所以, rust 允许模仿泛型数据类型,加入生命周期标志:

fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {if s2.len() > s1.len() {s2} else {s1}
}

这个函数中,声明了泛型生命周期 <'a> 。变量 s1、s2 在函数 longer 中拥有相同的生命周期标记 ‘a。如果多个输入变量拥有相同的生命周期标记,那么最终生命周期取其交集。这个例子中,’a 代表 s1、s2 生命周期的交集。函数 longer 的返回结果的生命周期也是 ‘a。于是我们知道,函数的返回结果的生命周期就是 s1、s2生命周期的交集。这样,函数 longer 输入和输出之间的生命周期推导链路就打通了。

现在,代码成了下面的样子。编译器可以畅通无阻地分析每个变量的生命周期以及是否存在冲突了。

fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {if s2.len() > s1.len() {s2} else {s1}
}fn main() {let r;let s1 = String::from("rust");let s2 = String::from("ecmascript");r = longer(&s1, &s2);println!("{} is longer", r);
}

3. 加入标记后的推理

把 main 函数修改一下,我们发现 longer 函数推导链路打通了,就可以判断 main 函数的代码是否合理了。下面的代码无法通过编译,因为 s1、s2 的生命周期无法覆盖 r 的生命周期,导致后面的 println 不安全。

fn main() {let r;{let s1 = String::from("rust");let s2 = String::from("ecmascript");r = longer(&s1, &s2);}println!("{} is longer", r);
}

把最后的 println 移入上面语句块,是的 r 与 s1、s2 生命周期相同,问题得到解决。修改后的 main 函数如下:

fn main() {let r;{let s1 = String::from("rust");let s2 = String::from("ecmascript");r = longer(&s1, &s2);println!("{} is longer", r);}
}

4. 生命周期:区间覆盖关系

生命周期可以存在嵌套关系,比如 ’a 可以覆盖 'b,可以标记为 'a : 'b

全局变量的生命周期为 'static,对于任何其他生命周期 ‘a,都有 'static : 'a

我们把上面的函数修改一下,要求生命周期 s1 覆盖 s2, s2 覆盖 r,如下面的代码,编译成功。

fn longer<'a, 'b, 'c>(s1: &'a str, s2: &'b str) -> &'c str where 'a : 'b, 'b : 'c 
{if s2.len() > s1.len() {s2} else {s1}
}fn main() {let s1 = String::from("rust");{let s2 = String::from("ecmascript");let r;r = longer(&s1, &s2);println!("{} is longer", r);}
}

这时,编译器不需要关心 longer 函数实现过程中的生命周期关系了,而是基于 main 函数代码推导变量之间生命周期的覆盖关系是否合理。上述代码因为 main 函数的逻辑正确,因此可以通过编译。

下面我做了个实验,下面再修改一下 longer 函数。我们要求 s1 的生命周期覆盖 s2 的生命周期,函数返回值的生命周期和 s1 相同。编译无法通过, 因为 s2 有可能成为返回结果,如果这件事情发生,s2的生命周期 'b 太短无法覆盖返回结果要求的生命周期 'a。:

fn longer<'a, 'b>(s1: &'a str, s2: &'b str) -> &'a str where 'a : 'b {if s2.len() > s1.len() {s2} else {s1}
}

如果把 where 'a : 'b 改成 where 'a : 'b, 'b : 'a ,实际上等于说 s1、s2 生命周期相同,这个函数又可以编译过去了。

5. 总结

  • 生命周期关系自动推断:一般情况下,编译器能够自动推断变量之间的生命周期覆盖是否合理。
  • 有些代码会打断生命周期推断的逻辑链路,这个时候就需要我们加入标记,弥补链路缺失,协助编译器来完成推断。

更多推荐

rust 生命周期很难理解,本文轻松帮你搞定!

本文发布于:2024-03-10 15:11:52,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1728310.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:很难   帮你   生命周期   本文   轻松

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!