前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >rust-生命周期

rust-生命周期

作者头像
潇洒
发布2023-10-23 14:58:17
1700
发布2023-10-23 14:58:17
举报
文章被收录于专栏:石头岛石头岛

前言

先说大白话,rust 的生命周期标注,是为了明确多个变量的生命周期是否一致,仅此而已,因为如果rust不知道多个变量的生命周期是否一致,它无法确的知道这个变量是否已经被释放。这个下面再细说,先说有什么用。

rust当中,的两个重要概念:借用生命周期分别代是在:

  1. 栈变量,需要关注【所有权】
  2. 引用(指针),需要关注【生命周期】

Rust 的每个引用都有自己的生命周期,生命周期指的是引用保持有效的作用域。 大多数情况下,引用是隐式的、可以被推断出来的,但当引用可能以不同的方式互相关联时,则需要手动标注生命周期。 这里重点就是以不同的方式互相关联时

大多数情况下,rust 可以自己推断出引用的生拿周期,也就是只有在一些rust无法自行推断的情况下,才需要手动标注生命周期。

生命周期

Rust 中的每一个引用都有其生命周期(lifetime),也就是引用保持有效的作用域。 大部分时候生命周期是隐含并可以推断的,正如大部分时候类型也是可以推断的一样。 类似于当因为有多种可能类型的时候必须注明类型,也会出现引用的生命周期以一些不同方式相关联的情况,所以 Rust 需要我们使用泛型生命周期参数来注明他们的关系,这样就能确保运行时实际使用的引用绝对是有效的。

这里还有一个需要关注的点就是关系,也就多个引用之前的关系,才是导致rust无法明确推断出引用生命周期的最根本原因。

反例

这段代码看着很正常,但是实际上,编译会报错,类为这里调用longest时,longest无法确认xy的生命周期。 为什么无法确认? 因为longest是被调用的方法,它肯定没法知道,这两个传入在main方法的中的生命周期。 好比,你写一个接口给外部调用,你也无法知道调你的服务,传入的两个变量,在那个服务中的生命周期。 但是在rust中,又非常强调安全性,它必须清楚每个引用的明确的生命周期。 所以这个活,就落在了开发者身上,必须明确告诉rust,每个引用的生命周期。

代码语言:javascript
复制
fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

// longest函数 无法确认 x、y  在 mian 函数中的生命周期
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

报错如下:missing lifetime specifier

代码语言:javascript
复制
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 {
  |           ++++     ++          ++          ++

For more information about this error, try `rustc --explain E0106`.
error: could not compile `playground` due to previous error

上面看着很正常呀,哪里有问题?

生命周期标注

即然rust不智能,那只能开发者辛苦一点,手动来标注了。 rust的生命周期标注语法,只能表示引用的生命周期,而不能、不会改会引用的生命周期。

命名规则:

  1. 'a 以 ' 开头
  2. 全小写
代码语言:javascript
复制
&i32        // 引用
&'a i32     // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用

单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系的。

函数签名中的生命周期注解

描述了 x、y 之间的关系。 longest 函数定义指定了签名中所有的引用必须有相同的生命周期'a

代码语言:javascript
复制
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
    }
}

只有一个参数,要不要标注

那当然是不要啦!! 生命周期注解告诉编译器引用参数的有效范围,以便编译器可以检查代码是否合法。 但是,在某些情况下,编译器可以自动推断出引用参数的生命周期,因此不需要显式注解。

当一个函数或方法需要一个借用参数时,如果该参数的生命周期与函数或方法的生命周期相同,则可以省略生命周期注解。例如: 这个例子,标不标注都是成立的。

代码语言:javascript
复制
fn foo<'a>(x: &'a i32) -> &'a i32 {
    x
}

fn main() {
    let x = 5;
    let y = foo(&x);
    println!("{}", y);
}

但是,如果函数或方法需要一个借用参数,并且该参数的生命周期与函数或方法的生命周期不同,则必须显式注解参数的生命周期。

代码语言:javascript
复制
struct Foo<'a> {
    x: &'a i32,
}

impl<'a> Foo<'a> {
    fn bar(&self, y: &'a i32) -> &'a i32 {
        if *y > 0 {
            y
        } else {
            self.x
        }
    }
}

fn main() {
    let x = 5;
    let y = 6;
    let foo = Foo { x: &x };
    let z = foo.bar(&y);
    println!("{}", z);
}

在这个例子中,方法 bar 的第二个参数 y 的生命周期不同于 Foo 结构体中的引用 x 的生命周期,所以嘛必须显式注解参数的生命周期。

总结

人多了,就容易产生纠分,变量形参多了,也是这样,所以才需要标注,分个明白。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 生命周期
  • 反例
  • 生命周期标注
  • 函数签名中的生命周期注解
  • 只有一个参数,要不要标注
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档