Rust进阶(3)-生命周期

生命周期

1. 概述

  1. 主要是针对引用
  2. Rust中每个引用都有其生命周期,也就是引用保持有效的作用域,大部分时候生命周期是隐含并可以推断的,正如大部分时候类型可以推断一样
  3. 生命周期的主要目标是避免悬垂引用
  4. Rust编译器使用借用检查器来检查生命周期是否有效
  5. 生命周期检测基本原理:检测变量的剩余生命周期是否一致,若不一致,则报错。
  6. 生命周期声明类似于变量类型声明,不会改变对象的真正生命周期。当你声明的生命周期和实际不符合的时候,编译器会报错。
  7. 声明生命周期:统一生命周期,按照最小生命周期来分析(说白了就是返回结果必须按照最小证明周期来声明,以防结果不安全)

悬垂引用介绍:

1
2
3
4
5
6
7
8
9
//r在作用域中已经被使用并释放,之后继续使用r,此时编译会异常,这就是垂直引用
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("r = {}",r);
}

2. 函数中的生命周期

为避免悬垂引用,需要使用声明'a来标注,确保生命周期一致。
注:因为对于编译器来说,如果不加上'a,运行期间,不确定返回的是x还是y,导致了生命周期的不确定性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

fn get_str<'a>(x: &'a str, y: & str) -> &'a str {
x
}

// 该方法是错误的,r返回后,会被释放,变成悬垂引用
fn a_str<'a>(x: &'a str, y: &'a str) -> &'a str {
let r = String::from("abc");
r.as_str()
}

fn main() {
let s1 = String::from("abcde");
let s2 = String::from("ab");
let r = longest(s1.as_str(), s2.as_str());
println!("r = {}", r);
}

3. 结构体中生命周期

1
2
3
4
5
6
7
8
9
10
#[derive(Debug)]
struct A<'a>{
name: &'a str,
}

fn main() {
let n = String::from("hello");
let a = A{name:&n};
println!("a = {:#?} ",a);
}

4. 生命周期省略

  1. 没有生命周期注解却能够编译。原因:早期的rust中必须显式的声明生命周期,后来rust团队将很明确的模式进行了注解的简化
  2. 遵守生命周期省略规则的情况下能明确变量的生命周期,则无需明确指定生命周期。函数或者方法的参数的生命周期称为输入生命周期,而返回的生命周期称为输出的生命周期
  3. 编译器采用三条规则判断引用何时不需要生命周期注解,当编译器检查完这三条规则后仍然不能计算出引用的生命周期,则会停止并生成错误。
  4. 生命周期注解省略规则适用于fn定义以及impl块定义,如:
    1. 每个引用的参数都有它自己的生命周期参数,例如:
      1. 一个引用参数的函数,其中有一个生命周期:fn foo<'a>(x:&'a i32)
      2. 两个引用参数的函数,则有两个生命周期:fn foo<'a,'b>(x:&'a i32,y: &'b i32)
    2. 如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:
      1. fn foo(x: &i32) -> &i32 等价于 fn foo<'a>(x: &'a i32) -> &'a i32
    3. 如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为&self或者&mut self,那么self的生命周期被赋予所有输出生命周期参数。(说白了就是,返回的结果要保证最小生命周期)

5. 方法中的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//方法中的生命周期
struct StuA<'a> {
name: &'a str,
}

impl<'a> StuA<'a> {
fn do_something(&self) -> i32 {
3
}
fn do_something2(&self, s: &str) -> &str {
self.name
}

//该方法异常,因为s的生命周期小于返回的(也就是self的),为s加上'a后,即可恢复正常(impl中已经声明,因此这里只需要给s加上'a即可)
/*fn do_something3(&self, s: &str) -> &str {
s
}*/
fn do_something3(&self, s: &'a str) -> &str {
s
}

}

fn main() {
let s = String::from("hello");
let a = StuA { name: &s };
/* let a = StuA { name: &s };
println!("{}", a.do_something());*/


/*let s = String::from("hello");
println!("{}", a.do_something2(&s));*/

let s = String::from("hello2");
println!("{}", a.do_something3(&s));
}

6. 静态生命周期

定义方式:'static
其生命周期存活于整个程序期间,所有的字符面值都拥有static生命周期
如:let s: &'static str = "hello";

7. 综合泛型生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::fmt::Display;

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

fn main() {
let s1 = String::from("i am s1");
let s2 = String::from("i am s2");
let ann = 129;
let r = function(s1.as_str(), s2.as_str(), ann);
println!("r = {}", r);
}

总结

本文编辑完毕

参考

[1] Rust 程序设计语言

  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信