Rust生命周期(lifetime)与引用有效性基础篇

2023年 8月 21日 77.1k 0

1. Rust生命周期(lifetime)是什么?

在 Rust 中,生命周期(Lifetime)是指一个变量或借用的有效时间范围。它指定了一个变量或借用在何时被创建,以及它在何时不再存在。Rust 中的每个变量或借用都有一个生命周期,它必须在其有效时间范围内被使用。如果一个变量或借用的生命周期结束了,那么它将不能再被访问,否则会引发编译时错误。生命周期通常由 Rust 中的编译器自动管理,开发者不需要手动管理生命周期。但是,开发者可以通过使用 Rust 的生命周期语法来指定生命周期,以实现更加精细的内存管理。

说明:

  • 生命周期结束,变量和借用生命周期结束
  • 生命周期由Rust自己掌管,开发人员无需手动管理

划重点:

  • Rust 中的每一个引用都有其 生命周期(lifetime),也就是引用保持有效的作用域。大部分时候生命周期是隐含并可以推断的,正如大部分时候类型也是可以推断的一样。

例子:

fn mxsm(x: &str) -> &str {
    x
}

上面的例子生命周期隐藏了。

2.生命周期(lifetime)标注

生命周期标注并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期。

生命周期参数名称必须以撇号(')开头,其名称通常全是小写,类似于泛型其名称非常短。'a 是大多数人默认使用的名称。生命周期参数标注位于引用的 & 之后,并有一个空格来将引用类型与生命周期标注分隔开。

说明:

  • 'static 是Rust内置的生命周期,相当于关键字。
  • 生命周期标注告诉 Rust 多个引用的泛型生命周期参数如何相互联系的

2.1 变量生命周期标注

生命周期标注语法:

&i32  //引用默认
&'a i32 //带有显示生命周期
&'a mut i32 // 带有显式生命周期的可变引用

2.2 函数签名中的生命周期标注

fn mxsm_fn(x: &str, y: &str) -> &str { //在开发工具中会直接提示报错
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

通过函数签名标注生命授权来消除编译错误:

fn mxsm_fn y.len() {
        x
    } else {
        y
    }
}

2.3结构体定义中的生命周期标注

struct mxsm{
    name: &str, //开发工具直接报错 缺少生命周期
    name_t: String //没有错误
}

从上面的代码可以验证一句话:Rust 中的每一个引用都有其 生命周期,有的时候被省略了而已。

结构体定义生命周期标注语法:

struct mxsm {
    fn mxsm_fn(&self)->i32{
        3
    }
    fn mxsm_ts(&self, tt:&str) -> &str{
        self.st
    }
}

2.5 Trait生命周期边界

语法:

Syntax
TypeParamBounds :
   TypeParamBound ( + TypeParamBound )* +?

TypeParamBound :
      Lifetime | TraitBound

TraitBound :
      ?? ForLifetimes? TypePath
   | ( ?? ForLifetimes? TypePath )

LifetimeBounds :
   ( Lifetime + )* Lifetime?

Lifetime :
      LIFETIME_OR_LABEL
   | 'static
   | '_

特征(Trait)和生命周期界限为泛型项目(generic item)提供了一种限制其参数类型和生命周期的方法。可以在 where 子句中为任何类型提供边界。对于某些常见情况,也有更简短的形式:

  • 在声明通用参数之后编写边界:fn f() {}fn f() where A: Copy {} 相同。
  • 在特征声明中作为超特征(supertrait):trait Circle: Shape {} 等同于 trait Circle where Self: Shape {}
  • 在特征声明中作为关联类型的边界:trait A { type B: Copy; } 等同于 trait A where Self::B: Copy { type B; }

在使用项目时,项目的边界必须得到满足。在对泛型项目进行类型检查和借用检查时,边界可用于确定类型是否实现了某个特征。例如,对于 Ty: Trait

  • 在泛型函数的主体中,可以对 Ty 值调用来自 Trait 的方法。同样,可以使用 Trait 上的关联常量。
  • 可以使用来自 Trait 的关联类型。
  • 可以使用带有 T: Trait 边界的泛型函数和类型,并将 Ty 用于 T

3. 生命周期省略

函数或方法的参数的生命周期被称为 输入生命周期(input lifetimes),而返回值的生命周期被称为 输出生命周期(output lifetimes)。

生命周期省略的三条规则:

  • 每一个是引用的参数都有它自己的生命周期参数

    fn mxsm_fn(x: &'a str, y: &'b str)
    fn mxsm_fn1 &i32
    fn mxsm &'a i32
    

    上面代码等同

  • 如果方法有多个输入生命周期参数并且其中一个参数是 &self&mut self,那么所有输出生命周期参数被赋予 self 的生命周期

  • 说明:方法第一个参数是**&self**&mut self ,说明是对象的方法。

    4.静态生命周期

    'static,其生命周期能够存活于整个程序期间。 所有的字符串字面量都拥有 'static 生命周期,如下面例子:

    let mxsm:&'static str = "mxsm world";
    

    4.1 'static 生命周期省略

    常量和静态引用类型的声明都隐式地具有’static’生命周期,除非明确指定生命周期。

    const STRING: &str = "bitstring"; //生命周期省略
    const STRING: &'static str = "bitstring"; //显示生命周期
    

    如果静态或常量项包括函数或闭包引用,而这些引用本身又包括引用,则编译器将首先尝试标准省略规则。如果它无法通过其通常的规则解决生命周期

    // Resolved as `fn &'a str`.
    const RESOLVED_SINGLE: fn(&str) -> &str = |x| x;
    
    // Resolved as `Fn usize`.
    const RESOLVED_MULTIPLE: &dyn Fn(&Foo, &Bar, &Baz) -> usize = &somefunc;
    

    5.总结

    Rust 的生命周期机制是一种确保代码正确性和安全性的重要机制。通过理解 Rust 的生命周期概念,开发者可以编写更加可靠和安全的代码。

    我是蚂蚁背大象,文章对你有帮助给项目点个❤关注我GitHub:mxsm,文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsm@apache.com

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论