每个引用值都有生命周期
概念
生命周期: 引用保持有效的一个作用域
- Rust的每个引用都有自己的生命周期
- 生命周期是隐式的、可被推断的
- 当引用的生命周期可能以不同的方式关联时:手动标注生命周期
生命周期存在的意义就是避免悬垂引用。
生命周期标注
首先明确一点:
生命周期标注并不会改变引用值的实际上的生命周期,也不会延长该生命周期,只是标注出一种关系。
// 生命周期在函数签名中的标注
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这里’a表示的意思其实就是x和y生命周期的交集,其实就是生命周期比较短的那个。
为了验证上面这句话,我们再来看一个例子
fn main() {
let s1 = String::from("hello");
let result;
{
let s2 = String::from("wold");
result = longest(s1.as_str(), s2.as_str());
}
print!("{}", result);
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// error[E0597]: `s2` does not live long enough
// --> src/main.rs:6:39
// |
// 6 | result = longest(s1.as_str(), s2.as_str());
// | ^^ borrowed value does not live long enough
// 7 | }
// | - `s2` dropped here while still borrowed
// 8 | print!("{}", result);
// | ------ borrow later used here
// error: aborting due to previous error
我们来分析一下:
因为'a
的生命周期是s1
和s2
中较短的那个,那么很明显这里的 'a = s2's life time;
这样的话就可以理解报错的内容了。因为生命周期'a
在花括号结束的时候就已经GG了,所以我们println打印的时候其实这个生命周期已经没了
这里再补充一个冷知识,如果let s2=”wold” 这样直接声明为字符切片的话,就不会报错。 因为字符切片的类型的是没有所有权的,字符切片直接存储与二进制文件中
深入理解生命周期
- 指定生命周期参数的方式依赖于函数所做的事情
我们这里还是采用上面的例子,上面的例子因为有可能返回x也有可能返回y是不确定的。所以需要指定两个参数的生命周期
但是我们这次明确的返回x,所以这时生命周期y就没必要指定了
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
从函数返回引用时,返回类型的生命周期参数需要与其中一个参数的生命周期匹配
在Rust引用分析中所编入的模式成为生命周期省略规则(就是一些通用可预测的模式)
- 这些规则无需开发者遵守
- 他们是一些特殊情况,由编译器来考虑
- 如果代码符合这些情况,就无需显示的标注生命周期
生命周期省略的三个规则
- 输入生命周期:函数或者方法的参数
- 输出生命周期:函数或者方法的返回值
- 规则1 应用于输入生命周期
- 规则2 应用于输出生命周期
- 如果编译器应用完这三个规则之后,仍然有无法确定的生命周期引用就会报错
- 这些规则适用于fn定义和impl块
- 规则1:每个引用类型的参数都有自己的生命周期
- 规则2:如果只有1个输入生命周期参数,那么该生命周期就会被赋予给所有的输出生命周期参数
- 规则3:如果有多个输入生命周期参数,但其中一个是&self或者&mut self(是方法),那么self的生命周期会被赋给所有的输出生命周期参数
静态生命周期
**
static**` 是一个特殊的生命周期:整个程序的持续时间
例如:
所有的字符串字面值都拥有static 生命周期,字符串字面值是直接被**存储在二进制文件中**的,所以他总是可用的
let s:&static str = "hello"
。