Rust学习第七天——生命周期

生命周期

  • Rust的每个引用都有自己的生命周期。

  • 生命周期:引用保持有效地作用域。

  • 大多数情况:生命周期是隐式的、可被推断的。

  • 当引用的生命周期可能以不同的方式互相关联时:手动标注生命周期。

生命周期-避免悬垂引用(dangling reference)

  • 生命周期的主要目标:避免悬垂引用

  • 例子:

下面这段代码报错,原因是r未初始化

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

借用检查器

  • Rust编译器的借用检查器:比较作用域来判断所有的借用是否合法。

函数中的泛型生命周期

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
    }
}

缺少命名的生命周期参数:

按照提示修改:

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
    }
}

生命周期标注语法

  • 生命周期的标注不会改变引用的生命周期长度

  • 当指定了泛型生命周期参数,函数可以接收带有仍和生命周期的引用

  • 生命周期的标注:描述了多个引用的生命周期的关系,但不会影响生命周期

生命周期标注-语法

  • 生命周期参数名:

-以'开头

-通常全小写且非常短

-很多人使用'a

  • 生命周期的标注位置:

-在引用的&符号后

-使用空格将标注和引用类型分开

生命周期标注-例子

  • &i32 //引用

  • &'a i32 //带有显式生命周期的引用

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

  • 单个生命周期的标注本身没有什么意义

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

  • 泛型生命周期参数声明在:函数名和参数列表之间的<>里

  • 生命周期'a的实际生命周期是:x和y两个生命周期中较小的那个

深入理解生命周期

  • 指定生命周期参数的方式依赖于函数所做的事情

  • 从函数返回引用时,返回类型的生命周期需要与其中一个参数的生命周期匹配

  • 如果返回的引用没有指向任何参数,那么它只能引用函数内创建的值

  • 这就是悬垂引用:该值在函数结束时走出了作用域

Struct定义中的生命周期标注

  • Struct里可包括:

  • 自持有的类型

  • 引用:需要在每个引用上添加生命周期标注

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago ……");
    
    let first_sentence = novel.split('.)
        .next()
        .expect("Could not found a '.'");
    
    let i = ImportantExcerpt{
        part: first_sentence
    };
}

生命周期的省略

  • 我们知道:

  • 每个引用都有生命周期

  • 需要为使用的生命周期的函数或Struct指定生命周期参数

  • (例子)

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {}

生命周期省略规则

  • 在Rust引用分析中所编入的模式称为生命周期省略规则。

  • 这些规则无需开发者来遵守

  • 它们是一些特殊情况,由编译器来考略

  • 如果你的代码符合这些情况,那么就无需显式标注生命周期

  • 生命周期短的省略规则不会提供完整的推断:

  • 如果应用规则后,引用的生命周期仍然模糊不清->编译错误

  • 解决办法:添加生命周期标注,标明引用间的相互关系

输入、输出生命周期

  • 生命周期在:

  • 函数/方法的参数:输入生命周期

  • 函数/方法的返回值:输出生命周期

生命周期省略的三个规则

  • 编译器使用3个规则在没有显式标注生命周期的情况下,来确定引用的生命周期

  • 规则1应用于输入生命周期

  • 规则2、3应用于输出生命周期

  • 如果编译器应用完3个规则之后,仍然有无法确定生命周期的引用->报错

  • 这些规则适用于fn定义和impl块

  • 规则1:每个引用类型的参数都有自己的生命周期

  • 规则2:如果只有1个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数

  • 规则3:如果有多个输入生命周期参数,但其中一个是&self或&mut self(是方法),那么self的生命周期会被赋给所有的输出生命周期

生命周期省略的三个规则-例子

  • 假设我们是编译器:

  • fn first_word(s: &str) -> &str {

  • fn first_word<'a>(s: &'a str) -> &str {

  • fn first_word<'a>(s: &'a str) -> &'a str {

  • ……

方法定义中的生命周期标注

  • 在struct上的生命周期实现方法,语法和泛型参数的语法一样

  • 在哪声明和使用生命周期参数,依赖于:

  • 生命周期参数是否和字段、方法的参数或返回值有关

  • struct字段的生命周期名:

  • 在impl后声明

  • 在struct名后使用

  • 这些生命周期是struct类型的一部分

  • impl块内的方法签名中:

  • 引用必须绑定于struct字段引用的生命周期,或者引用是独立的也可以

  • 生命周期省略规则经常使用方法中的生命周期标注不是必须的

struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }

    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago ……");

    let first_sentence = novel.split('.')
        .next()
        .expect("Could not found a '.'");

    let i = ImportantExcerpt{
        part: first_sentence
    };


}

静态生命周期

  • 'static'是一个特殊的生命周期:整个程序的持续时间。

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

  • 为引用指定'static生命周期前要三思:

  • 是否需要引用在程序整个生命周期内都存活。

泛型参数类型、Trait Bound、生命周期

(综合例子)

use std::fmt::Display;

fn longest_with_an_announce<'a, T>
    (x: &'a str, y: &'a str, ann: T) -> &'a str
where
    T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值